Merge branch 'main' into go/mad/convert-sinks

This commit is contained in:
Owen Mansel-Chan
2024-07-30 08:03:18 +01:00
committed by GitHub
98 changed files with 66377 additions and 485 deletions

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Improved performance of alias analysis of large function bodies. In rare cases, alerts that depend on alias analysis of large function bodies may be affected.

View File

@@ -215,19 +215,16 @@ predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { none() }
predicate localMustFlowStep(Node node1, Node node2) { none() }
/** Gets the type of `n` used for type pruning. */
Type getNodeType(Node n) {
DataFlowType getNodeType(Node n) {
exists(n) and
result instanceof VoidType // stub implementation
}
/** Gets a string representation of a type returned by `getNodeType`. */
string ppReprType(Type t) { none() } // stub implementation
/**
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
* a node of type `t1` to a node of type `t2`.
*/
predicate compatibleTypes(Type t1, Type t2) {
predicate compatibleTypes(DataFlowType t1, DataFlowType t2) {
t1 instanceof VoidType and t2 instanceof VoidType // stub implementation
}
@@ -243,7 +240,11 @@ class DataFlowCallable extends Function { }
class DataFlowExpr = Expr;
class DataFlowType = Type;
final private class TypeFinal = Type;
class DataFlowType extends TypeFinal {
string toString() { result = "" }
}
/** A function call relevant for data flow. */
class DataFlowCall extends Expr instanceof Call {

View File

@@ -994,9 +994,6 @@ DataFlowType getNodeType(Node n) {
result instanceof VoidType // stub implementation
}
/** Gets a string representation of a type returned by `getNodeType`. */
string ppReprType(DataFlowType t) { none() } // stub implementation
/**
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
* a node of type `t1` to a node of type `t2`.
@@ -1097,7 +1094,11 @@ class SummarizedCallable extends DataFlowCallable, TSummarizedCallable {
class DataFlowExpr = Expr;
class DataFlowType = Type;
final private class TypeFinal = Type;
class DataFlowType extends TypeFinal {
string toString() { result = "" }
}
cached
private newtype TDataFlowCall =

View File

@@ -227,13 +227,15 @@ private newtype TMemoryLocation =
TAllAliasedMemory(IRFunction irFunc, Boolean isMayAccess)
/**
* Represents the memory location accessed by a memory operand or memory result. In this implementation, the location is
* A memory location accessed by a memory operand or memory result. In this implementation, the location is
* one of the following:
* - `VariableMemoryLocation` - A location within a known `IRVariable`, at an offset that is either a constant or is
* unknown.
* - `UnknownMemoryLocation` - A location not known to be within a specific `IRVariable`.
*
* Some of these memory locations will be filtered out for performance reasons before being passed to SSA construction.
*/
abstract class MemoryLocation extends TMemoryLocation {
abstract private class MemoryLocation0 extends TMemoryLocation {
final string toString() {
if this.isMayAccess()
then result = "?" + this.toStringInternal()
@@ -294,9 +296,9 @@ abstract class MemoryLocation extends TMemoryLocation {
* represented by a `MemoryLocation` that totally overlaps all other
* `MemoryLocations` in the set.
*/
abstract class VirtualVariable extends MemoryLocation { }
abstract class VirtualVariable extends MemoryLocation0 { }
abstract class AllocationMemoryLocation extends MemoryLocation {
abstract class AllocationMemoryLocation extends MemoryLocation0 {
Allocation var;
boolean isMayAccess;
@@ -424,7 +426,7 @@ class VariableMemoryLocation extends TVariableMemoryLocation, AllocationMemoryLo
* `{a, b}` into a memory location that represents _all_ of the allocations
* in the set.
*/
class GroupedMemoryLocation extends TGroupedMemoryLocation, MemoryLocation {
class GroupedMemoryLocation extends TGroupedMemoryLocation, MemoryLocation0 {
VariableGroup vg;
boolean isMayAccess;
boolean isAll;
@@ -528,7 +530,7 @@ class GroupedVirtualVariable extends GroupedMemoryLocation, VirtualVariable {
/**
* An access to memory that is not known to be confined to a specific `IRVariable`.
*/
class UnknownMemoryLocation extends TUnknownMemoryLocation, MemoryLocation {
class UnknownMemoryLocation extends TUnknownMemoryLocation, MemoryLocation0 {
IRFunction irFunc;
boolean isMayAccess;
@@ -555,7 +557,7 @@ class UnknownMemoryLocation extends TUnknownMemoryLocation, MemoryLocation {
* An access to memory that is not known to be confined to a specific `IRVariable`, but is known to
* not access memory on the current function's stack frame.
*/
class AllNonLocalMemory extends TAllNonLocalMemory, MemoryLocation {
class AllNonLocalMemory extends TAllNonLocalMemory, MemoryLocation0 {
IRFunction irFunc;
boolean isMayAccess;
@@ -589,7 +591,7 @@ class AllNonLocalMemory extends TAllNonLocalMemory, MemoryLocation {
/**
* An access to all aliased memory.
*/
class AllAliasedMemory extends TAllAliasedMemory, MemoryLocation {
class AllAliasedMemory extends TAllAliasedMemory, MemoryLocation0 {
IRFunction irFunc;
boolean isMayAccess;
@@ -620,7 +622,7 @@ class AliasedVirtualVariable extends AllAliasedMemory, VirtualVariable {
/**
* Gets the overlap relationship between the definition location `def` and the use location `use`.
*/
Overlap getOverlap(MemoryLocation def, MemoryLocation use) {
Overlap getOverlap(MemoryLocation0 def, MemoryLocation0 use) {
exists(Overlap overlap |
// Compute the overlap based only on the extent.
overlap = getExtentOverlap(def, use) and
@@ -648,7 +650,7 @@ Overlap getOverlap(MemoryLocation def, MemoryLocation use) {
* based only on the set of memory locations accessed. Handling of "may" accesses and read-only
* locations occurs in `getOverlap()`.
*/
private Overlap getExtentOverlap(MemoryLocation def, MemoryLocation use) {
private Overlap getExtentOverlap(MemoryLocation0 def, MemoryLocation0 use) {
// The def and the use must have the same virtual variable, or no overlap is possible.
(
// AllAliasedMemory must totally overlap any location within the same virtual variable.
@@ -861,6 +863,40 @@ predicate canReuseSsaForOldResult(Instruction instr) { OldSsa::canReuseSsaForMem
bindingset[result, b]
private boolean unbindBool(boolean b) { result != b.booleanNot() }
/** Gets the number of overlapping uses of `def`. */
private int numberOfOverlappingUses(MemoryLocation0 def) {
result = strictcount(MemoryLocation0 use | exists(getOverlap(def, use)))
}
/**
* Holds if `def` is a busy definition. That is, it has a large number of
* overlapping uses.
*/
private predicate isBusyDef(MemoryLocation0 def) { numberOfOverlappingUses(def) > 1024 }
/** Holds if `use` is a use that overlaps with a busy definition. */
private predicate useOverlapWithBusyDef(MemoryLocation0 use) {
exists(MemoryLocation0 def |
exists(getOverlap(def, use)) and
isBusyDef(def)
)
}
final private class FinalMemoryLocation = MemoryLocation0;
/**
* A memory location accessed by a memory operand or memory result. In this implementation, the location is
* one of the following:
* - `VariableMemoryLocation` - A location within a known `IRVariable`, at an offset that is either a constant or is
* unknown.
* - `UnknownMemoryLocation` - A location not known to be within a specific `IRVariable`.
*
* Compared to `MemoryLocation0`, this class does not contain memory locations that represent uses of busy definitions.
*/
class MemoryLocation extends FinalMemoryLocation {
MemoryLocation() { not useOverlapWithBusyDef(this) }
}
MemoryLocation getResultMemoryLocation(Instruction instr) {
not canReuseSsaForOldResult(instr) and
exists(MemoryAccessKind kind, boolean isMayAccess |
@@ -905,9 +941,9 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
)
}
MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
private MemoryLocation0 getOperandMemoryLocation0(MemoryOperand operand, boolean isMayAccess) {
not canReuseSsaForOldResult(operand.getAnyDef()) and
exists(MemoryAccessKind kind, boolean isMayAccess |
exists(MemoryAccessKind kind |
kind = operand.getMemoryAccess() and
(if operand.hasMayReadMemoryAccess() then isMayAccess = true else isMayAccess = false) and
(
@@ -948,6 +984,19 @@ MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
)
}
MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
exists(MemoryLocation0 use0, boolean isMayAccess |
use0 = getOperandMemoryLocation0(operand, isMayAccess)
|
result = use0
or
// If `use0` overlaps with a busy definition we turn it into a use
// of `UnknownMemoryLocation`.
not use0 instanceof MemoryLocation and
result = TUnknownMemoryLocation(operand.getEnclosingIRFunction(), isMayAccess)
)
}
/** Gets the start bit offset of a `MemoryLocation`, if any. */
int getStartBitOffset(VariableMemoryLocation location) {
result = location.getStartBitOffset() and Ints::hasValue(result)

View File

@@ -51,6 +51,12 @@ private class Remquo extends Function, SideEffectFunction {
override predicate hasOnlySpecificReadSideEffects() { any() }
override predicate hasOnlySpecificWriteSideEffects() { any() }
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
this.getParameter(i).getUnspecifiedType() instanceof PointerType and
buffer = false and
mustWrite = true
}
}
private class Fma extends Function, SideEffectFunction {
@@ -95,4 +101,8 @@ private class Nan extends Function, SideEffectFunction, AliasFunction {
override predicate parameterNeverEscapes(int index) { index = 0 }
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
i = 0 and buffer = true
}
}

View File

@@ -10,11 +10,12 @@ contains sensitive data that could somehow be retrieved by an attacker.</p>
</overview>
<recommendation>
<p>Use alternative platform-supplied functions that will not get optimized away. Examples of such
functions include <code>memset_s</code>, <code>SecureZeroMemory</code>, and <code>bzero_explicit</code>.
Alternatively, passing the <code>-fno-builtin-memset</code> option to the GCC/Clang compiler usually
also prevents the optimization. Finally, you can use the public-domain <code>secure_memzero</code> function
(see references below). This function, however, is not guaranteed to work on all platforms and compilers.</p>
<p>Use <code>memset_s</code> (from C11) instead of <code>memset</code>, as <code>memset_s</code> will not
get optimized away. Alternatively use platform-supplied functions such as <code>SecureZeroMemory</code> or
<code>bzero_explicit</code> that make the same guarantee. Passing the <code>-fno-builtin-memset</code>
option to the GCC/Clang compiler usually also prevents the optimization. Finally, you can use the
public-domain <code>secure_memzero</code> function (see references below). This function, however, is not
guaranteed to work on all platforms and compilers.</p>
</recommendation>
<example>

View File

@@ -5,7 +5,7 @@
* @id cpp/unsigned-difference-expression-compared-zero
* @problem.severity warning
* @security-severity 9.8
* @precision medium
* @precision high
* @tags security
* correctness
* external/cwe/cwe-191

View File

@@ -1,12 +1,31 @@
void writeCredentials() {
char *password = "cleartext password";
FILE* file = fopen("credentials.txt", "w");
#include <sodium.h>
#include <stdio.h>
#include <string.h>
void writeCredentialsBad(FILE *file, const char *cleartextCredentials) {
// BAD: write password to disk in cleartext
fputs(password, file);
// GOOD: encrypt password first
char *encrypted = encrypt(password);
fputs(encrypted, file);
fputs(cleartextCredentials, file);
}
int writeCredentialsGood(FILE *file, const char *cleartextCredentials, const unsigned char *key, const unsigned char *nonce) {
size_t credentialsLen = strlen(cleartextCredentials);
size_t ciphertext_len = crypto_secretbox_MACBYTES + credentialsLen;
unsigned char *ciphertext = malloc(ciphertext_len);
if (!ciphertext) {
logError();
return -1;
}
// encrypt the password first
if (crypto_secretbox_easy(ciphertext, (const unsigned char *)cleartextCredentials, credentialsLen, nonce, key) != 0) {
free(ciphertext);
logError();
return -1;
}
// GOOD: write encrypted password to disk
fwrite(ciphertext, 1, ciphertext_len, file);
free(ciphertext);
return 0;
}

View File

@@ -19,15 +19,20 @@ cleartext.</p>
<example>
<p>The following example shows two ways of storing user credentials in a file. In the 'BAD' case,
the credentials are simply stored in cleartext. In the 'GOOD' case, the credentials are encrypted before
the credentials are simply stored in cleartext. In the 'GOOD' case, the credentials are encrypted before
storing them.</p>
<sample src="CleartextStorage.c" />
<p>Note that for the 'GOOD' example to work we need to link against an encryption library (in this case libsodium),
initialize it with a call to <code>sodium_init</code>, and create the key and nonce with
<code>crypto_secretbox_keygen</code> and <code>randombytes_buf</code> respectively. We also need to store those
details securely so they can be used for decryption.</p>
</example>
<references>
<li>M. Dowd, J. McDonald and J. Schuhm, <i>The Art of Software Security Assessment</i>, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.</li>
<li>M. Dowd, J. McDonald and J. Schuhm, <i>The Art of Software Security Assessment</i>, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.</li>
<li>M. Howard and D. LeBlanc, <i>Writing Secure Code</i>, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.</li>

View File

@@ -1,6 +1,6 @@
void bad(void) {
char *password = "cleartext password";
const char *password = "cleartext password";
sqlite3 *credentialsDB;
sqlite3_stmt *stmt;
@@ -16,14 +16,15 @@ void bad(void) {
}
}
void good(void) {
char *password = "cleartext password";
void good(const char *secretKey) {
const char *password = "cleartext password";
sqlite3 *credentialsDB;
sqlite3_stmt *stmt;
if (sqlite3_open("credentials.db", &credentialsDB) == SQLITE_OK) {
// GOOD: database encryption enabled:
sqlite3_exec(credentialsDB, "PRAGMA key = 'secretKey!'", NULL, NULL, NULL);
std::string setKeyString = std::string("PRAGMA key = '") + secretKey + "'";
sqlite3_exec(credentialsDB, setKeyString.c_str(), NULL, NULL, NULL);
sqlite3_exec(credentialsDB, "CREATE TABLE IF NOT EXISTS creds (password TEXT);", NULL, NULL, NULL);
if (sqlite3_prepare_v2(credentialsDB, "INSERT INTO creds(password) VALUES(?)", -1, &stmt, NULL) == SQLITE_OK) {
sqlite3_bind_text(stmt, 1, password, -1, SQLITE_TRANSIENT);
@@ -33,4 +34,3 @@ void good(void) {
}
}
}

View File

@@ -20,10 +20,12 @@ In the 'GOOD' case, the database (and thus the credentials) are encrypted.</p>
<sample src="CleartextSqliteDatabase.c" />
<p>Note that for the 'GOOD' example to work we need to provide a secret key. Secure key generation and storage is required.</p>
</example>
<references>
<li>M. Dowd, J. McDonald and J. Schuhm, <i>The Art of Software Security Assessment</i>, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.</li>
<li>M. Dowd, J. McDonald and J. Schuhm, <i>The Art of Software Security Assessment</i>, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.</li>
<li>M. Howard and D. LeBlanc, <i>Writing Secure Code</i>, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.</li>

View File

@@ -0,0 +1,4 @@
---
category: queryMetadata
---
* The precision of `cpp/unsigned-difference-expression-compared-zero` ("Unsigned difference expression compared to zero") has been increased to `high`. As a result, it will be run by default as part of the Code Scanning suite.

View File

@@ -1,7 +1,5 @@
| declspec.cpp:4:23:4:43 | Use fatal() instead | declspec.cpp:4:59:4:62 | exit | declspec.cpp:4:12:4:21 | deprecated | Use fatal() instead |
| routine_attributes2.cpp:5:6:5:11 | hidden | routine_attributes2.cpp:5:13:5:21 | a_routine | routine_attributes2.cpp:5:6:5:11 | visibility | hidden |
| routine_attributes2.cpp:5:6:5:11 | hidden | routine_attributes2.cpp:5:13:5:21 | a_routine | routine_attributes2.cpp:5:6:5:11 | visibility | hidden |
| routine_attributes2.h:3:6:3:11 | hidden | routine_attributes2.cpp:5:13:5:21 | a_routine | routine_attributes2.h:3:6:3:11 | visibility | hidden |
| routine_attributes2.h:3:6:3:11 | hidden | routine_attributes2.cpp:5:13:5:21 | a_routine | routine_attributes2.h:3:6:3:11 | visibility | hidden |
| routine_attributes.c:3:53:3:59 | dummy | routine_attributes.c:3:12:3:24 | named_weakref | routine_attributes.c:3:44:3:50 | weakref | dummy |
| routine_attributes.c:4:62:4:68 | dummy | routine_attributes.c:4:12:4:26 | aliased_weakref | routine_attributes.c:4:55:4:59 | alias | dummy |

View File

@@ -19,8 +19,6 @@
| header_export.cpp:18:6:18:16 | myFunction5 | header.h:10:2:10:10 | dllexport |
| header_export.cpp:18:6:18:16 | myFunction5 | header.h:10:2:10:10 | dllimport |
| routine_attributes2.cpp:5:13:5:21 | a_routine | routine_attributes2.cpp:5:6:5:11 | visibility |
| routine_attributes2.cpp:5:13:5:21 | a_routine | routine_attributes2.cpp:5:6:5:11 | visibility |
| routine_attributes2.cpp:5:13:5:21 | a_routine | routine_attributes2.h:3:6:3:11 | visibility |
| routine_attributes2.cpp:5:13:5:21 | a_routine | routine_attributes2.h:3:6:3:11 | visibility |
| routine_attributes.c:3:12:3:24 | named_weakref | routine_attributes.c:3:44:3:50 | weakref |
| routine_attributes.c:4:12:4:26 | aliased_weakref | routine_attributes.c:4:46:4:52 | weakref |

View File

@@ -1,6 +1,5 @@
| type_attributes2.cpp:5:14:5:20 | a_class | type_attributes2.cpp:5:7:5:12 | visibility | type_attributes2.cpp:5:7:5:12 | hidden |
| type_attributes2.cpp:5:14:5:20 | a_class | type_attributes2.h:3:7:3:12 | visibility | type_attributes2.h:3:7:3:12 | hidden |
| type_attributes2.cpp:5:14:5:20 | a_class | type_attributes2.h:3:7:3:12 | visibility | type_attributes2.h:3:7:3:12 | hidden |
| type_attributes_ms.cpp:4:67:4:75 | IDispatch | type_attributes_ms.cpp:4:19:4:22 | uuid | type_attributes_ms.cpp:4:24:4:63 | {00020400-0000-0000-c000-000000000046} |
| type_attributes_ms.cpp:5:30:5:33 | Str1 | type_attributes_ms.cpp:5:12:5:16 | align | type_attributes_ms.cpp:5:18:5:19 | 32 |
| type_attributes_ms.cpp:6:55:6:62 | IUnknown | type_attributes_ms.cpp:6:2:6:2 | uuid | type_attributes_ms.cpp:6:2:6:2 | 00000000-0000-0000-c000-000000000046 |

View File

@@ -1,7 +1,6 @@
| file://:0:0:0:0 | short __attribute((__may_alias__)) | type_attributes.c:25:30:25:42 | may_alias |
| type_attributes2.cpp:5:14:5:20 | a_class | type_attributes2.cpp:5:7:5:12 | visibility |
| type_attributes2.cpp:5:14:5:20 | a_class | type_attributes2.h:3:7:3:12 | visibility |
| type_attributes2.cpp:5:14:5:20 | a_class | type_attributes2.h:3:7:3:12 | visibility |
| type_attributes.c:5:36:5:51 | my_packed_struct | type_attributes.c:5:23:5:32 | packed |
| type_attributes.c:10:54:10:54 | (unnamed class/struct/union) | type_attributes.c:10:30:10:50 | transparent_union |
| type_attributes.c:16:54:16:54 | (unnamed class/struct/union) | type_attributes.c:16:30:16:50 | transparent_union |

View File

@@ -7,8 +7,6 @@
| ms_var_attributes.cpp:20:34:20:37 | pBuf | ms_var_attributes.cpp:20:12:20:12 | SAL_volatile |
| ms_var_attributes.h:5:22:5:27 | myInt3 | ms_var_attributes.h:5:1:5:9 | dllexport |
| var_attributes2.cpp:5:12:5:21 | a_variable | var_attributes2.cpp:5:5:5:10 | visibility |
| var_attributes2.cpp:5:12:5:21 | a_variable | var_attributes2.cpp:5:5:5:10 | visibility |
| var_attributes2.cpp:5:12:5:21 | a_variable | var_attributes2.h:3:12:3:17 | visibility |
| var_attributes2.cpp:5:12:5:21 | a_variable | var_attributes2.h:3:12:3:17 | visibility |
| var_attributes.c:1:12:1:19 | weak_var | var_attributes.c:1:36:1:39 | weak |
| var_attributes.c:2:12:2:22 | weakref_var | var_attributes.c:2:39:2:45 | weakref |

View File

@@ -496,6 +496,9 @@ getParameterTypeName
| stl.h:333:42:333:47 | insert | 1 | func:0 |
| stl.h:333:42:333:47 | insert | 2 | func:0 |
| stl.h:333:42:333:47 | insert | 2 | func:0 |
| stl.h:335:37:335:43 | emplace | 0 | const_iterator |
| stl.h:335:37:335:43 | emplace | 1 | func:0 && |
| stl.h:336:33:336:44 | emplace_back | 0 | func:0 && |
| stl.h:338:8:338:11 | swap | 0 | vector & |
| stl.h:351:12:351:21 | shared_ptr | 0 | class:0 * |
| stl.h:352:3:352:12 | shared_ptr | 0 | const shared_ptr & |
@@ -529,9 +532,25 @@ getParameterTypeName
| stl.h:435:6:435:15 | operator[] | 0 | key_type && |
| stl.h:435:6:435:15 | operator[] | 0 | key_type && |
| stl.h:436:6:436:7 | at | 0 | const key_type & |
| stl.h:439:48:439:54 | emplace | 0 | func:0 && |
| stl.h:439:48:439:54 | emplace | 0 | func:0 && |
| stl.h:440:36:440:47 | emplace_hint | 0 | const_iterator |
| stl.h:440:36:440:47 | emplace_hint | 0 | const_iterator |
| stl.h:440:36:440:47 | emplace_hint | 1 | func:0 && |
| stl.h:440:36:440:47 | emplace_hint | 1 | func:0 && |
| stl.h:443:24:443:29 | insert | 0 | value_type && |
| stl.h:445:12:445:17 | insert | 0 | const_iterator |
| stl.h:445:12:445:17 | insert | 1 | value_type && |
| stl.h:448:48:448:58 | try_emplace | 0 | key_type && |
| stl.h:448:48:448:58 | try_emplace | 0 | key_type && |
| stl.h:448:48:448:58 | try_emplace | 1 | func:0 && |
| stl.h:448:48:448:58 | try_emplace | 1 | func:0 && |
| stl.h:450:36:450:46 | try_emplace | 0 | const_iterator |
| stl.h:450:36:450:46 | try_emplace | 0 | const_iterator |
| stl.h:450:36:450:46 | try_emplace | 1 | key_type && |
| stl.h:450:36:450:46 | try_emplace | 1 | key_type && |
| stl.h:450:36:450:46 | try_emplace | 2 | func:0 && |
| stl.h:450:36:450:46 | try_emplace | 2 | func:0 && |
| stl.h:452:42:452:57 | insert_or_assign | 0 | key_type && |
| stl.h:452:42:452:57 | insert_or_assign | 1 | func:0 && |
| stl.h:454:30:454:45 | insert_or_assign | 0 | const_iterator |
@@ -550,9 +569,34 @@ getParameterTypeName
| stl.h:503:16:503:25 | operator[] | 0 | key_type && |
| stl.h:503:16:503:25 | operator[] | 0 | key_type && |
| stl.h:504:16:504:17 | at | 0 | const key_type & |
| stl.h:507:48:507:54 | emplace | 0 | func:0 && |
| stl.h:507:48:507:54 | emplace | 0 | func:0 && |
| stl.h:507:48:507:54 | emplace | 0 | func:0 && |
| stl.h:507:48:507:54 | emplace | 0 | func:0 && |
| stl.h:507:48:507:54 | emplace | 0 | func:0 && |
| stl.h:508:36:508:47 | emplace_hint | 0 | const_iterator |
| stl.h:508:36:508:47 | emplace_hint | 0 | const_iterator |
| stl.h:508:36:508:47 | emplace_hint | 1 | func:0 && |
| stl.h:508:36:508:47 | emplace_hint | 1 | func:0 && |
| stl.h:511:24:511:29 | insert | 0 | value_type && |
| stl.h:513:12:513:17 | insert | 0 | const_iterator |
| stl.h:513:12:513:17 | insert | 1 | value_type && |
| stl.h:516:48:516:58 | try_emplace | 0 | key_type && |
| stl.h:516:48:516:58 | try_emplace | 0 | key_type && |
| stl.h:516:48:516:58 | try_emplace | 0 | key_type && |
| stl.h:516:48:516:58 | try_emplace | 0 | key_type && |
| stl.h:516:48:516:58 | try_emplace | 0 | key_type && |
| stl.h:516:48:516:58 | try_emplace | 1 | func:0 && |
| stl.h:516:48:516:58 | try_emplace | 1 | func:0 && |
| stl.h:516:48:516:58 | try_emplace | 1 | func:0 && |
| stl.h:516:48:516:58 | try_emplace | 1 | func:0 && |
| stl.h:516:48:516:58 | try_emplace | 1 | func:0 && |
| stl.h:518:36:518:46 | try_emplace | 0 | const_iterator |
| stl.h:518:36:518:46 | try_emplace | 0 | const_iterator |
| stl.h:518:36:518:46 | try_emplace | 1 | key_type && |
| stl.h:518:36:518:46 | try_emplace | 1 | key_type && |
| stl.h:518:36:518:46 | try_emplace | 2 | func:0 && |
| stl.h:518:36:518:46 | try_emplace | 2 | func:0 && |
| stl.h:520:42:520:57 | insert_or_assign | 0 | key_type && |
| stl.h:520:42:520:57 | insert_or_assign | 1 | func:0 && |
| stl.h:522:30:522:45 | insert_or_assign | 0 | const_iterator |
@@ -567,6 +611,12 @@ getParameterTypeName
| stl.h:557:33:557:35 | set | 0 | func:0 |
| stl.h:557:33:557:35 | set | 1 | func:0 |
| stl.h:560:8:560:16 | operator= | 0 | const set & |
| stl.h:568:48:568:54 | emplace | 0 | func:0 && |
| stl.h:568:48:568:54 | emplace | 0 | func:0 && |
| stl.h:569:36:569:47 | emplace_hint | 0 | const_iterator |
| stl.h:569:36:569:47 | emplace_hint | 0 | const_iterator |
| stl.h:569:36:569:47 | emplace_hint | 1 | func:0 && |
| stl.h:569:36:569:47 | emplace_hint | 1 | func:0 && |
| stl.h:571:23:571:28 | insert | 0 | value_type && |
| stl.h:573:12:573:17 | insert | 0 | const_iterator |
| stl.h:573:12:573:17 | insert | 1 | value_type && |
@@ -584,6 +634,12 @@ getParameterTypeName
| stl.h:611:33:611:45 | unordered_set | 1 | func:0 |
| stl.h:611:33:611:45 | unordered_set | 2 | size_type |
| stl.h:614:18:614:26 | operator= | 0 | const unordered_set & |
| stl.h:622:48:622:54 | emplace | 0 | func:0 && |
| stl.h:622:48:622:54 | emplace | 0 | func:0 && |
| stl.h:623:36:623:47 | emplace_hint | 0 | const_iterator |
| stl.h:623:36:623:47 | emplace_hint | 0 | const_iterator |
| stl.h:623:36:623:47 | emplace_hint | 1 | func:0 && |
| stl.h:623:36:623:47 | emplace_hint | 1 | func:0 && |
| stl.h:625:24:625:29 | insert | 0 | value_type && |
| stl.h:627:12:627:17 | insert | 0 | const_iterator |
| stl.h:627:12:627:17 | insert | 1 | value_type && |

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -7,4 +7,61 @@ class TestConstexpr {
constexpr int member_const_constexpr() const { return 0; }
};
struct TestExplict {
explicit TestExplict();
};
template<typename T>
struct TestExplicitBool {
explicit(sizeof(T) == 1)
TestExplicitBool(const T);
};
explicit TestExplicitBool(const double) -> TestExplicitBool<int>;
template<typename T>
explicit(sizeof(T) == 4)
TestExplicitBool(const T) -> TestExplicitBool<int>;
void TestExplicitBoolFun() {
new TestExplicitBool<char>(0);
new TestExplicitBool<int>(0);
new TestExplicitBool(0.0f);
new TestExplicitBool(0.0);
}
template<typename T>
struct TestExplicitBool2 {
explicit(sizeof(T) == 1)
TestExplicitBool2(const T);
};
template<typename T>
TestExplicitBool2<T>::TestExplicitBool2(const T) { }
void TestExplicitBoolFun2() {
new TestExplicitBool2<char>(0);
new TestExplicitBool2<int>(0);
}
template<typename T>
struct TestExplicitBool3 {
template<typename U>
explicit(sizeof(T) == 1)
TestExplicitBool3(const T, const U);
};
template<typename T> template<typename U>
TestExplicitBool3<T>::TestExplicitBool3(const T, const U) { }
void TestExplicitBoolFun3() {
new TestExplicitBool3<char>(0, 0);
new TestExplicitBool3<int>(0, 0);
}
struct TestExplicitBool4 {
explicit(sizeof(char) == 1)
TestExplicitBool4(const char);
};
} // namespace cpp20

View File

@@ -0,0 +1,8 @@
| cpp20.cpp:15:8:15:23 | TestExplicitBool<char> | file://:0:0:0:0 | char | cpp20.cpp:17:5:17:20 | TestExplicitBool | explicit, extern, public |
| cpp20.cpp:15:8:15:23 | TestExplicitBool<int> | file://:0:0:0:0 | int | cpp20.cpp:17:5:17:20 | TestExplicitBool | extern, public |
| cpp20.cpp:34:8:34:24 | TestExplicitBool2<char> | file://:0:0:0:0 | char | cpp20.cpp:40:23:40:23 | TestExplicitBool2 | explicit, public |
| cpp20.cpp:34:8:34:24 | TestExplicitBool2<int> | file://:0:0:0:0 | int | cpp20.cpp:40:23:40:23 | TestExplicitBool2 | public |
| cpp20.cpp:48:8:48:24 | TestExplicitBool3<char> | file://:0:0:0:0 | char | cpp20.cpp:51:5:51:5 | TestExplicitBool3 | explicit, public |
| cpp20.cpp:48:8:48:24 | TestExplicitBool3<char> | file://:0:0:0:0 | char | cpp20.cpp:51:5:51:21 | TestExplicitBool3 | explicit, extern, public |
| cpp20.cpp:48:8:48:24 | TestExplicitBool3<int> | file://:0:0:0:0 | int | cpp20.cpp:51:5:51:5 | TestExplicitBool3 | public |
| cpp20.cpp:48:8:48:24 | TestExplicitBool3<int> | file://:0:0:0:0 | int | cpp20.cpp:51:5:51:21 | TestExplicitBool3 | extern, public |

View File

@@ -0,0 +1,7 @@
import cpp
from MemberFunction m, ClassTemplateInstantiation c, string specifiers
where
c.getAMember() = m and
specifiers = concat(string s | s = m.getASpecifier().getName() | s, ", ")
select c, c.getATemplateArgument(), m, specifiers

View File

@@ -1,146 +1,80 @@
| Class | specifiers2pp.cpp:8:7:8:13 | MyClass | MyClass | abstract |
| Class | specifiers2pp.cpp:24:7:24:14 | MyClass2 | MyClass2 | abstract |
| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | extern |
| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | extern |
| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | inline |
| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | inline |
| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | is_constexpr |
| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | is_constexpr |
| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | public |
| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | public |
| Function | cpp20.cpp:6:19:6:34 | member_constexpr | member_constexpr | declared_constexpr |
| Function | cpp20.cpp:6:19:6:34 | member_constexpr | member_constexpr | inline |
| Function | cpp20.cpp:6:19:6:34 | member_constexpr | member_constexpr | is_constexpr |
| Function | cpp20.cpp:6:19:6:34 | member_constexpr | member_constexpr | private |
| Function | cpp20.cpp:7:19:7:40 | member_const_constexpr | member_const_constexpr | const |
| Function | cpp20.cpp:7:19:7:40 | member_const_constexpr | member_const_constexpr | declared_constexpr |
| Function | cpp20.cpp:7:19:7:40 | member_const_constexpr | member_const_constexpr | inline |
| Function | cpp20.cpp:7:19:7:40 | member_const_constexpr | member_const_constexpr | is_constexpr |
| Function | cpp20.cpp:7:19:7:40 | member_const_constexpr | member_const_constexpr | private |
| Function | specifiers2.c:11:6:11:6 | f | f | c_linkage |
| Function | specifiers2.c:11:6:11:6 | f | f | extern |
| Function | specifiers2.c:12:13:12:13 | f | f | c_linkage |
| Function | specifiers2.c:12:13:12:13 | f | f | extern |
| Function | specifiers2.c:13:13:13:13 | f | f | c_linkage |
| Function | specifiers2.c:13:13:13:13 | f | f | extern |
| Function | specifiers2.c:15:13:15:13 | g | g | c_linkage |
| Function | specifiers2.c:15:13:15:13 | g | g | extern |
| Function | specifiers2.c:16:13:16:13 | g | g | c_linkage |
| Function | specifiers2.c:16:13:16:13 | g | g | extern |
| Function | specifiers2.c:21:6:21:12 | somefun | somefun | c_linkage |
| Function | specifiers2.c:21:6:21:12 | somefun | somefun | extern |
| Function | specifiers2.c:25:12:25:14 | add | add | c_linkage |
| Function | specifiers2.c:25:12:25:14 | add | add | inline |
| Function | specifiers2pp.cpp:8:7:8:7 | MyClass | MyClass | extern |
| Function | specifiers2pp.cpp:8:7:8:7 | MyClass | MyClass | extern |
| Function | specifiers2pp.cpp:8:7:8:7 | MyClass | MyClass | inline |
| Function | specifiers2pp.cpp:8:7:8:7 | MyClass | MyClass | inline |
| Function | specifiers2pp.cpp:8:7:8:7 | MyClass | MyClass | is_constexpr |
| Function | specifiers2pp.cpp:8:7:8:7 | MyClass | MyClass | is_constexpr |
| Function | specifiers2pp.cpp:8:7:8:7 | MyClass | MyClass | public |
| Function | specifiers2pp.cpp:8:7:8:7 | MyClass | MyClass | public |
| Function | specifiers2pp.cpp:8:7:8:7 | operator= | operator= | extern |
| Function | specifiers2pp.cpp:8:7:8:7 | operator= | operator= | extern |
| Function | specifiers2pp.cpp:8:7:8:7 | operator= | operator= | inline |
| Function | specifiers2pp.cpp:8:7:8:7 | operator= | operator= | inline |
| Function | specifiers2pp.cpp:8:7:8:7 | operator= | operator= | public |
| Function | specifiers2pp.cpp:8:7:8:7 | operator= | operator= | public |
| Function | specifiers2pp.cpp:10:18:10:24 | MyClass | MyClass | explicit |
| Function | specifiers2pp.cpp:10:18:10:24 | MyClass | MyClass | extern |
| Function | specifiers2pp.cpp:10:18:10:24 | MyClass | MyClass | public |
| Function | specifiers2pp.cpp:12:14:12:22 | publicFun | publicFun | inline |
| Function | specifiers2pp.cpp:12:14:12:22 | publicFun | publicFun | public |
| Function | specifiers2pp.cpp:13:21:13:26 | getInt | getInt | declared_virtual |
| Function | specifiers2pp.cpp:13:21:13:26 | getInt | getInt | extern |
| Function | specifiers2pp.cpp:13:21:13:26 | getInt | getInt | public |
| Function | specifiers2pp.cpp:13:21:13:26 | getInt | getInt | pure |
| Function | specifiers2pp.cpp:13:21:13:26 | getInt | getInt | virtual |
| Function | specifiers2pp.cpp:14:21:14:21 | f | f | declared_virtual |
| Function | specifiers2pp.cpp:14:21:14:21 | f | f | extern |
| Function | specifiers2pp.cpp:14:21:14:21 | f | f | public |
| Function | specifiers2pp.cpp:14:21:14:21 | f | f | virtual |
| Function | specifiers2pp.cpp:18:14:18:23 | privateFun | privateFun | inline |
| Function | specifiers2pp.cpp:18:14:18:23 | privateFun | privateFun | private |
| Function | specifiers2pp.cpp:21:14:21:25 | protectedFun | protectedFun | inline |
| Function | specifiers2pp.cpp:21:14:21:25 | protectedFun | protectedFun | protected |
| Function | specifiers2pp.cpp:24:7:24:7 | MyClass2 | MyClass2 | extern |
| Function | specifiers2pp.cpp:24:7:24:7 | MyClass2 | MyClass2 | extern |
| Function | specifiers2pp.cpp:24:7:24:7 | MyClass2 | MyClass2 | extern |
| Function | specifiers2pp.cpp:24:7:24:7 | MyClass2 | MyClass2 | inline |
| Function | specifiers2pp.cpp:24:7:24:7 | MyClass2 | MyClass2 | inline |
| Function | specifiers2pp.cpp:24:7:24:7 | MyClass2 | MyClass2 | inline |
| Function | specifiers2pp.cpp:24:7:24:7 | MyClass2 | MyClass2 | is_constexpr |
| Function | specifiers2pp.cpp:24:7:24:7 | MyClass2 | MyClass2 | is_constexpr |
| Function | specifiers2pp.cpp:24:7:24:7 | MyClass2 | MyClass2 | public |
| Function | specifiers2pp.cpp:24:7:24:7 | MyClass2 | MyClass2 | public |
| Function | specifiers2pp.cpp:24:7:24:7 | MyClass2 | MyClass2 | public |
| Function | specifiers2pp.cpp:24:7:24:7 | operator= | operator= | extern |
| Function | specifiers2pp.cpp:24:7:24:7 | operator= | operator= | extern |
| Function | specifiers2pp.cpp:24:7:24:7 | operator= | operator= | inline |
| Function | specifiers2pp.cpp:24:7:24:7 | operator= | operator= | inline |
| Function | specifiers2pp.cpp:24:7:24:7 | operator= | operator= | public |
| Function | specifiers2pp.cpp:24:7:24:7 | operator= | operator= | public |
| Function | specifiers2pp.cpp:26:21:26:21 | f | f | declared_virtual |
| Function | specifiers2pp.cpp:26:21:26:21 | f | f | extern |
| Function | specifiers2pp.cpp:26:21:26:21 | f | f | override |
| Function | specifiers2pp.cpp:26:21:26:21 | f | f | public |
| Function | specifiers2pp.cpp:26:21:26:21 | f | f | virtual |
| Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | extern |
| Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | extern |
| Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | inline |
| Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | inline |
| Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | is_constexpr |
| Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | is_constexpr |
| Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | public |
| Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | public |
| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | extern, inline, is_constexpr, public |
| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | extern, inline, is_constexpr, public |
| Function | cpp20.cpp:6:19:6:34 | member_constexpr | member_constexpr | declared_constexpr, inline, is_constexpr, private |
| Function | cpp20.cpp:7:19:7:40 | member_const_constexpr | member_const_constexpr | const, declared_constexpr, inline, is_constexpr, private |
| Function | cpp20.cpp:10:8:10:8 | TestExplict | TestExplict | extern, inline, is_constexpr, public |
| Function | cpp20.cpp:10:8:10:8 | TestExplict | TestExplict | extern, inline, is_constexpr, public |
| Function | cpp20.cpp:10:8:10:8 | operator= | operator= | extern, inline, is_constexpr, public |
| Function | cpp20.cpp:10:8:10:8 | operator= | operator= | extern, inline, is_constexpr, public |
| Function | cpp20.cpp:11:14:11:24 | TestExplict | TestExplict | explicit, extern, public |
| Function | cpp20.cpp:17:5:17:20 | TestExplicitBool | TestExplicitBool | explicit, extern, public |
| Function | cpp20.cpp:17:5:17:20 | TestExplicitBool | TestExplicitBool | extern, public |
| Function | cpp20.cpp:17:5:17:20 | TestExplicitBool | TestExplicitBool | extern, public |
| Function | cpp20.cpp:24:1:24:16 | TestExplicitBool | TestExplicitBool | has_trailing_return_type |
| Function | cpp20.cpp:40:1:40:39 | TestExplicitBool2 | TestExplicitBool2 | public |
| Function | cpp20.cpp:40:23:40:23 | TestExplicitBool2 | TestExplicitBool2 | explicit, public |
| Function | cpp20.cpp:40:23:40:23 | TestExplicitBool2 | TestExplicitBool2 | public |
| Function | cpp20.cpp:40:23:40:23 | TestExplicitBool2 | TestExplicitBool2 | public |
| Function | cpp20.cpp:51:5:51:5 | TestExplicitBool3 | TestExplicitBool3 | explicit, public |
| Function | cpp20.cpp:51:5:51:5 | TestExplicitBool3 | TestExplicitBool3 | public |
| Function | cpp20.cpp:51:5:51:21 | TestExplicitBool3 | TestExplicitBool3 | explicit, extern, public |
| Function | cpp20.cpp:51:5:51:21 | TestExplicitBool3 | TestExplicitBool3 | extern, public |
| Function | cpp20.cpp:55:1:55:39 | TestExplicitBool3 | TestExplicitBool3 | public |
| Function | cpp20.cpp:62:8:62:8 | TestExplicitBool4 | TestExplicitBool4 | extern, inline, is_constexpr, public |
| Function | cpp20.cpp:62:8:62:8 | TestExplicitBool4 | TestExplicitBool4 | extern, inline, is_constexpr, public |
| Function | cpp20.cpp:62:8:62:8 | operator= | operator= | extern, inline, is_constexpr, public |
| Function | cpp20.cpp:62:8:62:8 | operator= | operator= | extern, inline, is_constexpr, public |
| Function | cpp20.cpp:64:5:64:21 | TestExplicitBool4 | TestExplicitBool4 | explicit, extern, public |
| Function | file://:0:0:0:0 | operator delete | operator delete | extern |
| Function | file://:0:0:0:0 | operator new | operator new | extern |
| Function | specifiers2.c:11:6:11:6 | f | f | c_linkage, extern |
| Function | specifiers2.c:12:13:12:13 | f | f | c_linkage, extern |
| Function | specifiers2.c:13:13:13:13 | f | f | c_linkage, extern |
| Function | specifiers2.c:15:13:15:13 | g | g | c_linkage, extern |
| Function | specifiers2.c:16:13:16:13 | g | g | c_linkage, extern |
| Function | specifiers2.c:21:6:21:12 | somefun | somefun | c_linkage, extern |
| Function | specifiers2.c:25:12:25:14 | add | add | c_linkage, inline |
| Function | specifiers2pp.cpp:8:7:8:7 | MyClass | MyClass | extern, inline, is_constexpr, public |
| Function | specifiers2pp.cpp:8:7:8:7 | MyClass | MyClass | extern, inline, is_constexpr, public |
| Function | specifiers2pp.cpp:8:7:8:7 | operator= | operator= | extern, inline, public |
| Function | specifiers2pp.cpp:8:7:8:7 | operator= | operator= | extern, inline, public |
| Function | specifiers2pp.cpp:10:18:10:24 | MyClass | MyClass | explicit, extern, public |
| Function | specifiers2pp.cpp:12:14:12:22 | publicFun | publicFun | inline, public |
| Function | specifiers2pp.cpp:13:21:13:26 | getInt | getInt | declared_virtual, extern, public, pure, virtual |
| Function | specifiers2pp.cpp:14:21:14:21 | f | f | declared_virtual, extern, public, virtual |
| Function | specifiers2pp.cpp:18:14:18:23 | privateFun | privateFun | inline, private |
| Function | specifiers2pp.cpp:21:14:21:25 | protectedFun | protectedFun | inline, protected |
| Function | specifiers2pp.cpp:24:7:24:7 | MyClass2 | MyClass2 | extern, inline, is_constexpr, public |
| Function | specifiers2pp.cpp:24:7:24:7 | MyClass2 | MyClass2 | extern, inline, is_constexpr, public |
| Function | specifiers2pp.cpp:24:7:24:7 | MyClass2 | MyClass2 | extern, inline, public |
| Function | specifiers2pp.cpp:24:7:24:7 | operator= | operator= | extern, inline, public |
| Function | specifiers2pp.cpp:24:7:24:7 | operator= | operator= | extern, inline, public |
| Function | specifiers2pp.cpp:26:21:26:21 | f | f | declared_virtual, extern, override, public, virtual |
| Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | extern, inline, is_constexpr, public |
| Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | extern, inline, is_constexpr, public |
| Function | specifiers2pp.cpp:33:5:33:18 | fun | fun | public |
| Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | extern |
| Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | extern |
| Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | inline |
| Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | inline |
| Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | is_constexpr |
| Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | is_constexpr |
| Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | public |
| Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | public |
| Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | extern, inline, is_constexpr, public |
| Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | extern, inline, is_constexpr, public |
| Function | specifiers2pp.cpp:40:12:40:18 | someFun | someFun | extern |
| Function | specifiers2pp.cpp:41:16:41:23 | someFun2 | someFun2 | c_linkage |
| Function | specifiers2pp.cpp:41:16:41:23 | someFun2 | someFun2 | extern |
| Function | specifiers2pp.cpp:43:9:43:16 | someFun3 | someFun3 | c_linkage |
| Function | specifiers2pp.cpp:43:9:43:16 | someFun3 | someFun3 | extern |
| Function | specifiers2pp.cpp:44:16:44:23 | someFun4 | someFun4 | c_linkage |
| Function | specifiers2pp.cpp:44:16:44:23 | someFun4 | someFun4 | static |
| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | extern |
| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | extern |
| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | inline |
| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | inline |
| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | is_constexpr |
| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | is_constexpr |
| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | public |
| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | public |
| Function | specifiers2pp.cpp:63:19:63:34 | member_constexpr | member_constexpr | const |
| Function | specifiers2pp.cpp:63:19:63:34 | member_constexpr | member_constexpr | declared_constexpr |
| Function | specifiers2pp.cpp:63:19:63:34 | member_constexpr | member_constexpr | inline |
| Function | specifiers2pp.cpp:63:19:63:34 | member_constexpr | member_constexpr | is_constexpr |
| Function | specifiers2pp.cpp:63:19:63:34 | member_constexpr | member_constexpr | private |
| Function | specifiers2pp.cpp:64:19:64:40 | member_const_constexpr | member_const_constexpr | const |
| Function | specifiers2pp.cpp:64:19:64:40 | member_const_constexpr | member_const_constexpr | declared_constexpr |
| Function | specifiers2pp.cpp:64:19:64:40 | member_const_constexpr | member_const_constexpr | inline |
| Function | specifiers2pp.cpp:64:19:64:40 | member_const_constexpr | member_const_constexpr | is_constexpr |
| Function | specifiers2pp.cpp:64:19:64:40 | member_const_constexpr | member_const_constexpr | private |
| FunctionDeclarationEntry | specifiers2.c:11:6:11:6 | declaration of f | f | c_linkage |
| FunctionDeclarationEntry | specifiers2.c:11:6:11:6 | declaration of f | f | void_param_list |
| FunctionDeclarationEntry | specifiers2.c:12:13:12:13 | declaration of f | f | c_linkage |
| FunctionDeclarationEntry | specifiers2.c:12:13:12:13 | declaration of f | f | extern |
| FunctionDeclarationEntry | specifiers2.c:12:13:12:13 | declaration of f | f | void_param_list |
| FunctionDeclarationEntry | specifiers2.c:13:13:13:13 | declaration of f | f | c_linkage |
| FunctionDeclarationEntry | specifiers2.c:13:13:13:13 | declaration of f | f | extern |
| FunctionDeclarationEntry | specifiers2.c:13:13:13:13 | declaration of f | f | void_param_list |
| FunctionDeclarationEntry | specifiers2.c:15:13:15:13 | declaration of g | g | c_linkage |
| FunctionDeclarationEntry | specifiers2.c:15:13:15:13 | declaration of g | g | extern |
| FunctionDeclarationEntry | specifiers2.c:15:13:15:13 | declaration of g | g | void_param_list |
| FunctionDeclarationEntry | specifiers2.c:16:13:16:13 | declaration of g | g | c_linkage |
| FunctionDeclarationEntry | specifiers2.c:16:13:16:13 | declaration of g | g | extern |
| FunctionDeclarationEntry | specifiers2.c:16:13:16:13 | declaration of g | g | void_param_list |
| Function | specifiers2pp.cpp:41:16:41:23 | someFun2 | someFun2 | c_linkage, extern |
| Function | specifiers2pp.cpp:43:9:43:16 | someFun3 | someFun3 | c_linkage, extern |
| Function | specifiers2pp.cpp:44:16:44:23 | someFun4 | someFun4 | c_linkage, static |
| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | extern, inline, is_constexpr, public |
| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | extern, inline, is_constexpr, public |
| Function | specifiers2pp.cpp:63:19:63:34 | member_constexpr | member_constexpr | const, declared_constexpr, inline, is_constexpr, private |
| Function | specifiers2pp.cpp:64:19:64:40 | member_const_constexpr | member_const_constexpr | const, declared_constexpr, inline, is_constexpr, private |
| FunctionDeclarationEntry | cpp20.cpp:11:14:11:24 | declaration of TestExplict | TestExplict | explicit |
| FunctionDeclarationEntry | cpp20.cpp:40:23:40:23 | definition of TestExplicitBool2 | TestExplicitBool2 | explicit |
| FunctionDeclarationEntry | cpp20.cpp:51:5:51:5 | definition of TestExplicitBool3 | TestExplicitBool3 | explicit |
| FunctionDeclarationEntry | cpp20.cpp:51:5:51:21 | declaration of TestExplicitBool3 | TestExplicitBool3 | explicit |
| FunctionDeclarationEntry | cpp20.cpp:64:5:64:21 | declaration of TestExplicitBool4 | TestExplicitBool4 | explicit |
| FunctionDeclarationEntry | specifiers2.c:11:6:11:6 | declaration of f | f | c_linkage, void_param_list |
| FunctionDeclarationEntry | specifiers2.c:12:13:12:13 | declaration of f | f | c_linkage, extern, void_param_list |
| FunctionDeclarationEntry | specifiers2.c:13:13:13:13 | declaration of f | f | c_linkage, extern, void_param_list |
| FunctionDeclarationEntry | specifiers2.c:15:13:15:13 | declaration of g | g | c_linkage, extern, void_param_list |
| FunctionDeclarationEntry | specifiers2.c:16:13:16:13 | declaration of g | g | c_linkage, extern, void_param_list |
| FunctionDeclarationEntry | specifiers2.c:21:6:21:12 | declaration of somefun | somefun | c_linkage |
| FunctionDeclarationEntry | specifiers2.c:25:12:25:14 | definition of add | add | c_linkage |
| FunctionDeclarationEntry | specifiers2pp.cpp:2:6:2:9 | definition of afun | afun | void_param_list |
@@ -154,28 +88,19 @@
| FunctionDeclarationEntry | specifiers2pp.cpp:31:13:31:15 | declaration of fun | fun | void_param_list |
| FunctionDeclarationEntry | specifiers2pp.cpp:33:5:33:18 | definition of fun | fun | void_param_list |
| FunctionDeclarationEntry | specifiers2pp.cpp:40:12:40:18 | declaration of someFun | someFun | extern |
| FunctionDeclarationEntry | specifiers2pp.cpp:41:16:41:23 | declaration of someFun2 | someFun2 | c_linkage |
| FunctionDeclarationEntry | specifiers2pp.cpp:41:16:41:23 | declaration of someFun2 | someFun2 | extern |
| FunctionDeclarationEntry | specifiers2pp.cpp:43:9:43:16 | declaration of someFun3 | someFun3 | c_linkage |
| FunctionDeclarationEntry | specifiers2pp.cpp:43:9:43:16 | declaration of someFun3 | someFun3 | extern |
| FunctionDeclarationEntry | specifiers2pp.cpp:44:16:44:23 | declaration of someFun4 | someFun4 | c_linkage |
| FunctionDeclarationEntry | specifiers2pp.cpp:44:16:44:23 | declaration of someFun4 | someFun4 | static |
| FunctionDeclarationEntry | specifiers2pp.cpp:41:16:41:23 | declaration of someFun2 | someFun2 | c_linkage, extern |
| FunctionDeclarationEntry | specifiers2pp.cpp:43:9:43:16 | declaration of someFun3 | someFun3 | c_linkage, extern |
| FunctionDeclarationEntry | specifiers2pp.cpp:44:16:44:23 | declaration of someFun4 | someFun4 | c_linkage, static |
| TypedefType | file://:0:0:0:0 | Const | Const | const |
| TypedefType | specifiers2.c:27:19:27:26 | constInt | constInt | const |
| TypedefType | specifiers2pp.cpp:47:20:47:32 | const_pointer | const_pointer | const |
| TypedefType | specifiers2pp.cpp:49:32:49:53 | volatile_const_pointer | volatile_const_pointer | const |
| TypedefType | specifiers2pp.cpp:49:32:49:53 | volatile_const_pointer | volatile_const_pointer | volatile |
| TypedefType | specifiers2pp.cpp:50:32:50:54 | volatile_const_pointer2 | volatile_const_pointer2 | const |
| TypedefType | specifiers2pp.cpp:50:32:50:54 | volatile_const_pointer2 | volatile_const_pointer2 | volatile |
| TypedefType | specifiers2pp.cpp:53:23:53:45 | volatile_const_pointer3 | volatile_const_pointer3 | const |
| TypedefType | specifiers2pp.cpp:53:23:53:45 | volatile_const_pointer3 | volatile_const_pointer3 | volatile |
| TypedefType | specifiers2pp.cpp:54:44:54:74 | volatile_const_restrict_pointer | volatile_const_restrict_pointer | const |
| TypedefType | specifiers2pp.cpp:54:44:54:74 | volatile_const_restrict_pointer | volatile_const_restrict_pointer | restrict |
| TypedefType | specifiers2pp.cpp:54:44:54:74 | volatile_const_restrict_pointer | volatile_const_restrict_pointer | volatile |
| TypedefType | specifiers2pp.cpp:49:32:49:53 | volatile_const_pointer | volatile_const_pointer | const, volatile |
| TypedefType | specifiers2pp.cpp:50:32:50:54 | volatile_const_pointer2 | volatile_const_pointer2 | const, volatile |
| TypedefType | specifiers2pp.cpp:53:23:53:45 | volatile_const_pointer3 | volatile_const_pointer3 | const, volatile |
| TypedefType | specifiers2pp.cpp:54:44:54:74 | volatile_const_restrict_pointer | volatile_const_restrict_pointer | const, restrict, volatile |
| TypedefType | specifiers2pp.cpp:56:28:56:32 | Const | Const | const |
| TypedefType | specifiers2pp.cpp:58:7:58:15 | Const_int | Const_int | const |
| TypedefType | specifiers2pp.cpp:60:28:60:45 | volatile_Const_int | volatile_Const_int | const |
| TypedefType | specifiers2pp.cpp:60:28:60:45 | volatile_Const_int | volatile_Const_int | volatile |
| TypedefType | specifiers2pp.cpp:60:28:60:45 | volatile_Const_int | volatile_Const_int | const, volatile |
| Variable | specifiers2.c:8:12:8:12 | j | j | extern |
| Variable | specifiers2.c:9:12:9:12 | j | j | extern |
| Variable | specifiers2.c:18:21:18:23 | svi | svi | static |

View File

@@ -14,16 +14,20 @@ string interesting(Element e) {
e instanceof VariableDeclarationEntry and result = "VariableDeclarationEntry"
}
from Element e, string name, string specifier
from Element e, string name, string specifiers
where
(
name = e.(Declaration).getName() or
name = e.(DeclarationEntry).getName() or
name = e.(Type).toString()
) and
(
specifier = e.(Declaration).getASpecifier().toString() or
specifier = e.(DeclarationEntry).getASpecifier() or
specifier = e.(TypedefType).getBaseType().getASpecifier().toString()
)
select interesting(e), e, name, specifier
specifiers =
concat(string s |
s = e.(Declaration).getASpecifier().toString() or
s = e.(DeclarationEntry).getASpecifier() or
s = e.(TypedefType).getBaseType().getASpecifier().toString()
|
s, ", "
) and
specifiers != ""
select interesting(e), e, name, specifiers

View File

@@ -1,3 +1,22 @@
| cpp20.cpp:17:28:17:28 | (unnamed parameter 0) | file://:0:0:0:0 | const T | file://:0:0:0:0 | const |
| cpp20.cpp:17:28:17:28 | (unnamed parameter 0) | file://:0:0:0:0 | const char | file://:0:0:0:0 | const |
| cpp20.cpp:17:28:17:28 | (unnamed parameter 0) | file://:0:0:0:0 | const int | file://:0:0:0:0 | const |
| cpp20.cpp:24:24:24:24 | (unnamed parameter 0) | file://:0:0:0:0 | const T | file://:0:0:0:0 | const |
| cpp20.cpp:40:47:40:47 | (unnamed parameter 0) | file://:0:0:0:0 | const T | file://:0:0:0:0 | const |
| cpp20.cpp:40:47:40:47 | (unnamed parameter 0) | file://:0:0:0:0 | const char | file://:0:0:0:0 | const |
| cpp20.cpp:40:47:40:47 | (unnamed parameter 0) | file://:0:0:0:0 | const int | file://:0:0:0:0 | const |
| cpp20.cpp:51:29:51:29 | (unnamed parameter 0) | file://:0:0:0:0 | const char | file://:0:0:0:0 | const |
| cpp20.cpp:51:29:51:29 | (unnamed parameter 0) | file://:0:0:0:0 | const char | file://:0:0:0:0 | const |
| cpp20.cpp:51:29:51:29 | (unnamed parameter 0) | file://:0:0:0:0 | const int | file://:0:0:0:0 | const |
| cpp20.cpp:51:29:51:29 | (unnamed parameter 0) | file://:0:0:0:0 | const int | file://:0:0:0:0 | const |
| cpp20.cpp:51:38:51:38 | (unnamed parameter 1) | file://:0:0:0:0 | const U | file://:0:0:0:0 | const |
| cpp20.cpp:51:38:51:38 | (unnamed parameter 1) | file://:0:0:0:0 | const U | file://:0:0:0:0 | const |
| cpp20.cpp:51:38:51:38 | (unnamed parameter 1) | file://:0:0:0:0 | const int | file://:0:0:0:0 | const |
| cpp20.cpp:51:38:51:38 | (unnamed parameter 1) | file://:0:0:0:0 | const int | file://:0:0:0:0 | const |
| cpp20.cpp:55:47:55:47 | (unnamed parameter 0) | file://:0:0:0:0 | const T | file://:0:0:0:0 | const |
| cpp20.cpp:55:56:55:56 | (unnamed parameter 1) | file://:0:0:0:0 | const U | file://:0:0:0:0 | const |
| cpp20.cpp:64:23:64:32 | (unnamed parameter 0) | file://:0:0:0:0 | const char | file://:0:0:0:0 | const |
| file://:0:0:0:0 | (unnamed parameter 0) | file://:0:0:0:0 | const double | file://:0:0:0:0 | const |
| specifiers2.c:18:21:18:23 | svi | file://:0:0:0:0 | volatile int | file://:0:0:0:0 | volatile |
| specifiers2.c:19:11:19:12 | ci | file://:0:0:0:0 | const int | file://:0:0:0:0 | const |
| specifiers2.c:21:34:21:34 | p | file://:0:0:0:0 | char *__restrict__ | file://:0:0:0:0 | restrict |

View File

@@ -0,0 +1,4 @@
| cpp20.cpp:17:5:17:20 | TestExplicitBool | extern, public |
| cpp20.cpp:40:1:40:39 | TestExplicitBool2 | public |
| cpp20.cpp:40:23:40:23 | TestExplicitBool2 | public |
| cpp20.cpp:55:1:55:39 | TestExplicitBool3 | public |

View File

@@ -0,0 +1,7 @@
import cpp
from MemberFunction m, TemplateClass c, string specifiers
where
c.getAMember() = m and
specifiers = concat(string s | s = m.getASpecifier().getName() | s, ", ")
select m, specifiers

View File

@@ -581,3 +581,15 @@ void test46()
*rP = nullptr;
use(r);
}
namespace std {
float remquo(float, float, int*);
}
void test47() {
float x = 1.0f;
float y = 2.0f;
int quo;
std::remquo(x, y, &quo);
use(quo); // GOOD
}

View File

@@ -2444,9 +2444,6 @@ DataFlowType getNodeType(Node n) {
] = result.getADelegateCreation()
}
/** Gets a string representation of a `DataFlowType`. */
string ppReprType(DataFlowType t) { result = t.toString() }
private class DataFlowNullType extends Gvn::GvnType {
DataFlowNullType() { this = Gvn::getGlobalValueNumber(any(NullType nt)) }

View File

@@ -0,0 +1,30 @@
.. _codeql-cli-2.17.6:
==========================
CodeQL 2.17.6 (2024-06-27)
==========================
.. contents:: Contents
:depth: 2
:local:
:backlinks: none
This is an overview of changes in the CodeQL CLI and relevant CodeQL query and library packs. For additional updates on changes to the CodeQL code scanning experience, check out the `code scanning section on the GitHub blog <https://github.blog/tag/code-scanning/>`__, `relevant GitHub Changelog updates <https://github.blog/changelog/label/code-scanning/>`__, `changes in the CodeQL extension for Visual Studio Code <https://marketplace.visualstudio.com/items/GitHub.vscode-codeql/changelog>`__, and the `CodeQL Action changelog <https://github.com/github/codeql-action/blob/main/CHANGELOG.md>`__.
Security Coverage
-----------------
CodeQL 2.17.6 runs a total of 414 security queries when configured with the Default suite (covering 161 CWE). The Extended suite enables an additional 131 queries (covering 35 more CWE).
CodeQL CLI
----------
New Features
~~~~~~~~~~~~
* Beta support is now available for analyzing C# codebases without needing a working build. To use this, pass the :code:`--build-mode none` option to :code:`codeql database create`.
Improvements
~~~~~~~~~~~~
* The :code:`--model-packs` option is now publicly available. This option allows commands like :code:`codeql database analyze` to accept a list of model packs that are used to augment the analysis of all queries involved in the analysis.

View File

@@ -0,0 +1,178 @@
.. _codeql-cli-2.18.0:
==========================
CodeQL 2.18.0 (2024-07-11)
==========================
.. contents:: Contents
:depth: 2
:local:
:backlinks: none
This is an overview of changes in the CodeQL CLI and relevant CodeQL query and library packs. For additional updates on changes to the CodeQL code scanning experience, check out the `code scanning section on the GitHub blog <https://github.blog/tag/code-scanning/>`__, `relevant GitHub Changelog updates <https://github.blog/changelog/label/code-scanning/>`__, `changes in the CodeQL extension for Visual Studio Code <https://marketplace.visualstudio.com/items/GitHub.vscode-codeql/changelog>`__, and the `CodeQL Action changelog <https://github.com/github/codeql-action/blob/main/CHANGELOG.md>`__.
Security Coverage
-----------------
CodeQL 2.18.0 runs a total of 415 security queries when configured with the Default suite (covering 161 CWE). The Extended suite enables an additional 131 queries (covering 35 more CWE). 1 security query has been added with this release.
CodeQL CLI
----------
Breaking Changes
~~~~~~~~~~~~~~~~
* A number of breaking changes have been made to the C and C++ CodeQL environment:
* The environment no longer defines any GNU-specific builtin macros.
If these macros are still needed, please define them via
:code:`semmle-extractor-options`.
* The :code:`--force-recompute` option is no longer directly supported by
:code:`semmle-extractor-options`. Instead, :code:`--edg --force-recompute` should be specified.
* The :code:`--gnu_version` and :code:`--microsoft_version` options that can be specified via :code:`semmle-extractor-options` are now synonyms, and only one should be specified as part of :code:`semmle-extractor-options`.
Furthermore, is also no longer possible to specify these options via the following syntax.
* :code:`--edg --gnu_version --edg <version number>`, and
* :code:`--edg --microsoft_version --edg <version number>`
The shorter :code:`--gnu_version <version number>` and
:code:`--microsoft_version <version number>` should be used.
* The :code:`--build_error_dir` and :code:`--predefined_macros` command line options have been removed from the C/C++ extractor. It has never been possible to pass these options through the CLI, but some customers with advanced setups may have been passing them through internal undocumented interfaces.
Passing the option :code:`--build_error_dir` did not have any effect, and it is safe to remove the option. The :code:`--predefined_macros` option should have been unnecessary, as long as the extractor was invoked with the
:code:`--mimic` option.
Bug Fixes
~~~~~~~~~
* Where a MacOS unsigned binary cannot be signed, CodeQL will now continue trying to trace compiler invocations created by that process and its children. In particular this means that Bazel builds on MacOS are now traceable.
* Fixed a bug where test discovery would fail if there is a syntax error in a qlpack file. Now, a warning message will be printed and discovery will continue.
Improvements
~~~~~~~~~~~~
* Introduced the :code:`--include-logs` option to the :code:`codeql database bundle` command. This new feature allows users to include logs in the generated database bundle, allowing for a more complete treatment of the bundle, and bringing the tool capabilities up-to-speed with the documentation.
* :code:`codeql database init` and :code:`codeql database create` now support the
:code:`--force-overwrite` option. When this option is specified, the command will delete the specified database directory even if it does not look like a database directory. This option is only recommended for automation. For directcommand line commands, it is recommended to use the :code:`--overwrite` option, which includes extra protection and will refuse to delete a directory that does not look like a database directory.
* Extract :code:`.xsaccess`, :code:`*.xsjs` and :code:`*.xsjslib` files for SAP HANA XS as Javascript.
* We have updated many compiler error messages and warnings to improve their readability and standardize their grammar.
Where necessary, please use the :code:`--learn` option for the :code:`codeql test run` command.
Known Issues
~~~~~~~~~~~~
* Compilation of QL queries is about 30% slower than in previous releases. This only affects users who write custom queries, and only at compilation time, not at run time. This regression will be fixed in the upcoming 2.18.1 release.
Query Packs
-----------
Major Analysis Improvements
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Java
""""
* The query :code:`java/weak-cryptographic-algorithm` no longer alerts about :code:`RSA/ECB` algorithm strings.
Minor Analysis Improvements
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Java
""""
* The query :code:`java/tainted-permissions-check` now uses threat models. This means that :code:`local` sources are no longer included by default for this query, but can be added by enabling the :code:`local` threat model.
* Added more :code:`org.apache.commons.io.FileUtils`\ -related sinks to the path injection query.
JavaScript/TypeScript
"""""""""""""""""""""
* Added a new experimental query, :code:`js/cors-misconfiguration`, which detects misconfigured CORS HTTP headers in the :code:`cors` and :code:`apollo` libraries.
Python
""""""
* Adding Python support for Hardcoded Credentials as Models as Data
* Additional sanitizers have been added to the :code:`py/full-ssrf` and :code:`py/partial-ssrf` queries for methods that verify a string contains only a certain set of characters, such as :code:`.isalnum()` as well as regular expression tests.
Language Libraries
------------------
Bug Fixes
~~~~~~~~~
Golang
""""""
* Fixed dataflow via global variables other than via a direct write: for example, via a side-effect on a global, such as :code:`io.copy(SomeGlobal, ...)` or via assignment to a field or array or slice cell of a global. This means that any data-flow query may return more results where global variables are involved.
Java
""""
* Support for :code:`codeql test run` for Kotlin sources has been fixed.
Major Analysis Improvements
~~~~~~~~~~~~~~~~~~~~~~~~~~~
JavaScript/TypeScript
"""""""""""""""""""""
* Added support for TypeScript 5.5.
Minor Analysis Improvements
~~~~~~~~~~~~~~~~~~~~~~~~~~~
C/C++
"""""
* The queries "Potential double free" (:code:`cpp/double-free`) and "Potential use after free" (:code:`cpp/use-after-free`) now produce fewer false positives.
* The "Guards" library (:code:`semmle.code.cpp.controlflow.Guards`) now also infers guards from calls to the builtin operation :code:`__builtin_expect`. As a result, some queries may produce fewer false positives.
Golang
""""""
* DataFlow queries which previously used :code:`RemoteFlowSource` to define their sources have been modified to instead use :code:`ThreatModelFlowSource`. This means these queries will now respect threat model configurations. The default threat model configuration is equivalent to :code:`RemoteFlowSource`, so there should be no change in results for users using the default.
* Added the :code:`ThreatModelFlowSource` class to :code:`FlowSources.qll`. The :code:`ThreatModelFlowSource` class can be used to include sources which match the current *threat model* configuration. This is the first step in supporting threat modeling for Go.
Java
""""
* Added models for the following packages:
* io.undertow.server.handlers.resource
* jakarta.faces.context
* javax.faces.context
* javax.servlet
* org.jboss.vfs
* org.springframework.core.io
* A bug has been fixed in the heuristic identification of uncertain control flow, which is used to filter data flow in order to improve performance and reduce false positives. This fix means that slightly more code is identified and hence pruned from data flow.
* Excluded reverse DNS from the loopback address as a source of untrusted data.
JavaScript/TypeScript
"""""""""""""""""""""
* Enabled type-tracking to follow content through array methods
* Improved modeling of :code:`Array.prototype.splice` for when it is called with more than two arguments
Python
""""""
* A number of Python queries now support sinks defined using data extensions. The format of data extensions for Python has been documented.
Ruby
""""
* Element references with blocks, such as :code:`foo[:bar] { |x| puts x}`, are now parsed correctly.
* The :code:`CleartextSources.qll` library, used by :code:`rb/clear-text-logging-sensitive-data` and :code:`rb/clear-text-logging-sensitive-data`, has been updated to consider heuristics for additional categories of sensitive data.
New Features
~~~~~~~~~~~~
C/C++
"""""
* The syntax for models-as-data rows has been extended to make it easier to select sources, sinks, and summaries that involve templated functions and classes. Additionally, the syntax has also been extended to make it easier to specify models with arbitrary levels of indirection. See :code:`dataflow/ExternalFlow.qll` for the updated documentation and specification for the model format.
* It is now possible to extend the classes :code:`AllocationFunction` and :code:`DeallocationFunction` via data extensions. Extensions of these classes should be added to the :code:`lib/ext/allocation` and :code:`lib/ext/deallocation` directories respectively.

View File

@@ -0,0 +1,173 @@
.. _codeql-cli-2.18.1:
==========================
CodeQL 2.18.1 (2024-07-25)
==========================
.. contents:: Contents
:depth: 2
:local:
:backlinks: none
This is an overview of changes in the CodeQL CLI and relevant CodeQL query and library packs. For additional updates on changes to the CodeQL code scanning experience, check out the `code scanning section on the GitHub blog <https://github.blog/tag/code-scanning/>`__, `relevant GitHub Changelog updates <https://github.blog/changelog/label/code-scanning/>`__, `changes in the CodeQL extension for Visual Studio Code <https://marketplace.visualstudio.com/items/GitHub.vscode-codeql/changelog>`__, and the `CodeQL Action changelog <https://github.com/github/codeql-action/blob/main/CHANGELOG.md>`__.
Security Coverage
-----------------
CodeQL 2.18.1 runs a total of 419 security queries when configured with the Default suite (covering 164 CWE). The Extended suite enables an additional 129 queries (covering 34 more CWE). 2 security queries have been added with this release.
CodeQL CLI
----------
New Features
~~~~~~~~~~~~
* The *experimental* type :code:`QlBuiltins::BigInt` of arbitrary-precision integers has been introduced. To opt in to this API, compile your queries with
:code:`--allow-experimental=bigint`. Big integers can be constructed using the
:code:`.toBigInt()` methods of :code:`int` and :code:`string`. The built-in operations are:
* comparisons: :code:`=`, :code:`!=`, :code:`<`, :code:`<=`, :code:`>`, :code:`>=`,
* conversions: :code:`.toString()`, :code:`.toInt()`,
* arithmetic: binary :code:`+`, :code:`-`, :code:`*`, :code:`/`, :code:`%`, unary :code:`-`,
* bitwise operations: :code:`.bitAnd(BigInt)`, :code:`.bitOr(BigInt)`,
:code:`.bitXor(BigInt)`, :code:`.bitShiftLeft(int)`, :code:`.bitShiftRightSigned(int)`,
:code:`.bitNot()`,
* aggregates: :code:`min`, :code:`max`, (:code:`strict`):code:`sum`, (:code:`strict`):code:`count`, :code:`avg`,
:code:`rank`, :code:`unique`, :code:`any`.
* other: :code:`.pow(int)`, :code:`.abs()`, :code:`.gcd(BigInt)`, :code:`.minimum(BigInt)`,
:code:`.maximum(BigInt)`.
* :code:`codeql test run` now supports postprocessing of test results. When .qlref files specify a path to a :code:`postprocess` query, then this is evaluated after the test query to transform the test outputs prior to concatenating them into the :code:`actual` results.
Improvements
~~~~~~~~~~~~
* The 30% QL query compilation slowdown noted in 2.18.0 has been fixed.
Security Updates
~~~~~~~~~~~~~~~~
* Resolves CVE-2023-4759, an arbitrary file overwrite in Eclipse JGit that can be triggered when using untrusted third-party queries from a git repository. See the
\ `security advisory <https://github.com/github/codeql-cli-binaries/security/advisories/GHSA-x4gx-f2xv-6wj9>`__ for more information.
* The following dependencies have been updated. These updates include security fixes in the respective libraries that prevent out-of-bounds accesses or denial-of-service in scenarios where untrusted files are processed. These scenarios are not likely to be encountered in most uses of CodeQL and code scanning, and only apply to advanced use cases where precompiled query packs,
database ZIP files, or database TRAP files are obtained from untrusted sources and then processed on a trusted machine.
* airlift/aircompressor is updated to version 0.27.
* Apache Ant is updated to version 1.10.11.
* Apache Commons Compress is updated to version 1.26.0.
* Apache Commons IO is updated to version 2.15.1.
* Apache Commons Lang3 is updated to version 3.14.0.
* jsoup is updated to version 1.15.3.
* Logback is updated to version 1.2.13.
* Snappy is updated to version 0.5.
Query Packs
-----------
Minor Analysis Improvements
~~~~~~~~~~~~~~~~~~~~~~~~~~~
C/C++
"""""
* The :code:`cpp/unsigned-difference-expression-compared-zero` ("Unsigned difference expression compared to zero") query now produces fewer false positives.
Java
""""
* The heuristic to enable certain Android queries has been improved. Now it ignores Android Manifests which don't define an activity, content provider or service. We also only consider files which are under a folder containing such an Android Manifest for these queries. This should remove some false positive alerts.
JavaScript/TypeScript
"""""""""""""""""""""
* Added a new query, :code:`js/functionality-from-untrusted-domain`, which detects uses in HTML and JavaScript scripts from untrusted domains, including the :code:`polyfill.io` content delivery network
* it can be extended to detect other compromised scripts using user-provided data extensions of the :code:`untrustedDomain` predicate, which takes one string argument with the domain to warn on (and will warn on any subdomains too).
* Modified existing query, :code:`js/functionality-from-untrusted-source`, to allow adding this new query, but reusing the same logic
* Added the ability to use data extensions to require SRI on CDN hostnames using the :code:`isCdnDomainWithCheckingRequired` predicate, which takes one string argument of the full hostname to require SRI for.
* Created a new library, :code:`semmle.javascript.security.FunctionalityFromUntrustedSource`, to support both queries.
New Queries
~~~~~~~~~~~
JavaScript/TypeScript
"""""""""""""""""""""
* Added a new query, :code:`js/insecure-helmet-configuration`, to detect instances where Helmet middleware is configured with important security features disabled.
Query Metadata Changes
~~~~~~~~~~~~~~~~~~~~~~
C/C++
"""""
* The precision of :code:`cpp/iterator-to-expired-container` ("Iterator to expired container") has been increased to :code:`high`. As a result, it will be run by default as part of the Code Scanning suite.
* The precision of :code:`cpp/unsafe-strncat` ("Potentially unsafe call to strncat") has been increased to :code:`high`. As a result, it will be run by default as part of the Code Scanning suite.
Language Libraries
------------------
Breaking Changes
~~~~~~~~~~~~~~~~
Java
""""
* The Java extractor no longer supports the :code:`SEMMLE_DIST` legacy environment variable.
Minor Analysis Improvements
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Golang
""""""
* There was a bug which meant that the built-in function :code:`clear` was considered as a sanitizer in some cases when it shouldn't have been. This has now been fixed, which may lead to more alerts.
Java
""""
* Added a path-injection sink for :code:`hudson.FilePath.exists()`.
* Added summary models for :code:`org.apache.commons.io.IOUtils.toByteArray`.
* Java build-mode :code:`none` analyses now only report a warning on the CodeQL status page when there are significant analysis problems-- defined as 5% of expressions lacking a type, or 5% of call targets being unknown. Other messages reported on the status page are downgraded from warnings to notes and so are less prominent, but are still available for review.
Python
""""""
* Additional modelling to detect direct writes to the :code:`Set-Cookie` header has been added for several web frameworks.
Swift
"""""
* Additional heuristics for sensitive private information have been added to the :code:`SensitiveExprs.qll` library, improving coverage for credit card and social security numbers. This may result in additional results for queries that use sensitive data such as :code:`swift/cleartext-transmission`.
Deprecated APIs
~~~~~~~~~~~~~~~
Java
""""
* The predicate :code:`isAndroid` from the module :code:`semmle.code.java.security.AndroidCertificatePinningQuery` has been deprecated. Use :code:`semmle.code.java.frameworks.android.Android::inAndroidApplication(File)` instead.
New Features
~~~~~~~~~~~~
C/C++
"""""
* Models-as-data alert provenance information has been extended to the C/C++ language. Any qltests that include the edges relation in their output (for example, :code:`.qlref`\ s that reference path-problem queries) will need to be have their expected output updated accordingly.
* Added subclasses of :code:`BuiltInOperations` for :code:`__builtin_has_attribute`, :code:`__builtin_is_corresponding_member`, :code:`__builtin_is_pointer_interconvertible_with_class`, :code:`__is_assignable_no_precondition_check`, :code:`__is_bounded_array`, :code:`__is_convertible`, :code:`__is_corresponding_member`, :code:`__is_nothrow_convertible`, :code:`__is_pointer_interconvertible_with_class`, :code:`__is_referenceable`, :code:`__is_same_as`, :code:`__is_trivially_copy_assignable`, :code:`__is_unbounded_array`, :code:`__is_valid_winrt_type`, :code:`_is_win_class`, :code:`__is_win_interface`, :code:`__reference_binds_to_temporary`, :code:`__reference_constructs_from_temporary`, and :code:`__reference_converts_from_temporary`.
* The class :code:`NewArrayExpr` adds a predicate :code:`getArraySize()` to allow a more convenient way to access the static size of the array when the extent is missing.
Java and Kotlin
"""""""""""""""
* Kotlin support is now out of beta, and generally available
* Kotlin versions up to 2.0.2*x* are now supported.
Swift
"""""
* Swift support is now out of beta, and generally available.

View File

@@ -11,6 +11,9 @@ A list of queries for each suite and language `is available here <https://docs.g
.. toctree::
:maxdepth: 1
codeql-cli-2.18.1
codeql-cli-2.18.0
codeql-cli-2.17.6
codeql-cli-2.17.5
codeql-cli-2.17.4
codeql-cli-2.17.3

View File

@@ -14,19 +14,11 @@
Windows,"Windows 10 / Windows Server 2019
Windows 11 / Windows Server 2022","x86-64"
macOS,"macOS 10.15 Catalina
macOS 11 Big Sur
macOS 12 Monterey
macOS,"macOS 12 Monterey
macOS 13 Ventura
macOS 14 Sonoma","x86-64
x86-64, arm64 (Apple Silicon)
x86-64, arm64 (Apple Silicon)
macOS 14 Sonoma","x86-64, arm64 (Apple Silicon)
x86-64, arm64 (Apple Silicon)

View File

@@ -548,12 +548,9 @@ DataFlowType getNodeType(Node n)
```
and every `Node` should have a type.
One also needs to define the string representation of a `DataFlowType`:
```ql
string ppReprType(DataFlowType t)
```
The `ppReprType` predicate is used for printing a type in the labels of
`PathNode`s, this can be defined as `none()` if type pruning is not used.
One also needs to define the string representation of a `DataFlowType`.
The `DataFlowType.toString` predicate is used for printing a type in the labels of
`PathNode`s, this should be defined as `result = ""` if type pruning is not used.
Finally, one must define `CastNode` as a subclass of `Node` as those nodes
where types should be checked. Usually this will be things like explicit casts.

View File

@@ -215,9 +215,6 @@ predicate localMustFlowStep(Node node1, Node node2) { none() }
/** Gets the type of `n` used for type pruning. */
DataFlowType getNodeType(Node n) { result = TTodoDataFlowType() and exists(n) }
/** Gets a string representation of a type returned by `getNodeType()`. */
string ppReprType(DataFlowType t) { none() }
/**
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
* a node of type `t1` to a node of type `t2`.

View File

@@ -0,0 +1,18 @@
/**
* @kind test-postprocess
*/
import codeql.dataflow.test.ProvenancePathGraph
import semmle.go.dataflow.ExternalFlow
external predicate queryResults(string relation, int row, int column, string data);
external predicate queryRelations(string relation);
query predicate resultRelations(string relation) { queryRelations(relation) }
module Res = TranslateProvenanceResults<interpretModelForTest/2, queryResults/4>;
from string relation, int row, int column, string data
where Res::results(relation, row, column, data)
select relation, row, column, data

View File

@@ -1,14 +0,0 @@
/**
* @kind path-problem
*/
import go
import semmle.go.security.TaintedPath
import codeql.dataflow.test.ProvenancePathGraph
import semmle.go.dataflow.ExternalFlow
import ShowProvenance<interpretModelForTest/2, TaintedPath::Flow::PathNode, TaintedPath::Flow::PathGraph>
from TaintedPath::Flow::PathNode source, TaintedPath::Flow::PathNode sink
where TaintedPath::Flow::flowPath(source, sink)
select sink.getNode(), source, sink, "This path depends on a $@.", source.getNode(),
"user-provided value"

View File

@@ -0,0 +1,2 @@
query: Security/CWE-022/TaintedPath.ql
postprocess: TestUtilities/PrettyPrintModels.ql

View File

@@ -1 +1,2 @@
Security/CWE-022/UnsafeUnzipSymlink.ql
query: Security/CWE-022/UnsafeUnzipSymlink.ql
postprocess: TestUtilities/PrettyPrintModels.ql

View File

@@ -1,3 +1,8 @@
#select
| UnsafeUnzipSymlinkGood.go:72:3:72:25 | ... := ...[0] | UnsafeUnzipSymlinkGood.go:72:3:72:25 | ... := ...[0] | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | Unsanitized archive entry, which may contain '..', is used in a $@. | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | file system operation |
| ZipSlip.go:11:2:15:2 | range statement[1] | ZipSlip.go:11:2:15:2 | range statement[1] | ZipSlip.go:14:20:14:20 | p | Unsanitized archive entry, which may contain '..', is used in a $@. | ZipSlip.go:14:20:14:20 | p | file system operation |
| tarslip.go:15:2:15:30 | ... := ...[0] | tarslip.go:15:2:15:30 | ... := ...[0] | tarslip.go:16:14:16:34 | call to Dir | Unsanitized archive entry, which may contain '..', is used in a $@. | tarslip.go:16:14:16:34 | call to Dir | file system operation |
| tst.go:23:2:43:2 | range statement[1] | tst.go:23:2:43:2 | range statement[1] | tst.go:29:20:29:23 | path | Unsanitized archive entry, which may contain '..', is used in a $@. | tst.go:29:20:29:23 | path | file system operation |
edges
| UnsafeUnzipSymlinkGood.go:52:24:52:32 | definition of candidate | UnsafeUnzipSymlinkGood.go:61:53:61:61 | candidate | provenance | |
| UnsafeUnzipSymlinkGood.go:61:53:61:61 | candidate | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | provenance | FunctionModel |
@@ -7,10 +12,13 @@ edges
| UnsafeUnzipSymlinkGood.go:76:70:76:80 | selection of Name | UnsafeUnzipSymlinkGood.go:52:24:52:32 | definition of candidate | provenance | |
| ZipSlip.go:11:2:15:2 | range statement[1] | ZipSlip.go:12:24:12:29 | selection of Name | provenance | |
| ZipSlip.go:12:3:12:30 | ... := ...[0] | ZipSlip.go:14:20:14:20 | p | provenance | |
| ZipSlip.go:12:24:12:29 | selection of Name | ZipSlip.go:12:3:12:30 | ... := ...[0] | provenance | MaD:945 |
| ZipSlip.go:12:24:12:29 | selection of Name | ZipSlip.go:12:3:12:30 | ... := ...[0] | provenance | MaD:1 |
| tarslip.go:15:2:15:30 | ... := ...[0] | tarslip.go:16:23:16:33 | selection of Name | provenance | |
| tarslip.go:16:23:16:33 | selection of Name | tarslip.go:16:14:16:34 | call to Dir | provenance | MaD:960 |
| tarslip.go:16:23:16:33 | selection of Name | tarslip.go:16:14:16:34 | call to Dir | provenance | MaD:2 |
| tst.go:23:2:43:2 | range statement[1] | tst.go:29:20:29:23 | path | provenance | |
models
| 1 | Summary: path/filepath; ; false; Abs; ; ; Argument[0]; ReturnValue[0]; taint; manual |
| 2 | Summary: path; ; false; Dir; ; ; Argument[0]; ReturnValue; taint; manual |
nodes
| UnsafeUnzipSymlinkGood.go:52:24:52:32 | definition of candidate | semmle.label | definition of candidate |
| UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | semmle.label | call to Join |
@@ -28,8 +36,3 @@ nodes
| tst.go:23:2:43:2 | range statement[1] | semmle.label | range statement[1] |
| tst.go:29:20:29:23 | path | semmle.label | path |
subpaths
#select
| UnsafeUnzipSymlinkGood.go:72:3:72:25 | ... := ...[0] | UnsafeUnzipSymlinkGood.go:72:3:72:25 | ... := ...[0] | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | Unsanitized archive entry, which may contain '..', is used in a $@. | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | file system operation |
| ZipSlip.go:11:2:15:2 | range statement[1] | ZipSlip.go:11:2:15:2 | range statement[1] | ZipSlip.go:14:20:14:20 | p | Unsanitized archive entry, which may contain '..', is used in a $@. | ZipSlip.go:14:20:14:20 | p | file system operation |
| tarslip.go:15:2:15:30 | ... := ...[0] | tarslip.go:15:2:15:30 | ... := ...[0] | tarslip.go:16:14:16:34 | call to Dir | Unsanitized archive entry, which may contain '..', is used in a $@. | tarslip.go:16:14:16:34 | call to Dir | file system operation |
| tst.go:23:2:43:2 | range statement[1] | tst.go:23:2:43:2 | range statement[1] | tst.go:29:20:29:23 | path | Unsanitized archive entry, which may contain '..', is used in a $@. | tst.go:29:20:29:23 | path | file system operation |

View File

@@ -1 +1,2 @@
Security/CWE-022/ZipSlip.ql
query: Security/CWE-022/ZipSlip.ql
postprocess: TestUtilities/PrettyPrintModels.ql

View File

@@ -78,13 +78,11 @@ public class OdasaOutput {
}
public OdasaOutput(boolean trackClassOrigins, Compression compression, Logger log) {
String trapFolderVar = Env.systemEnv().getFirstNonEmpty("CODEQL_EXTRACTOR_JAVA_TRAP_DIR",
Var.TRAP_FOLDER.name());
String trapFolderVar = Env.systemEnv().get("CODEQL_EXTRACTOR_JAVA_TRAP_DIR");
if (trapFolderVar == null) {
throw new ResourceError("CODEQL_EXTRACTOR_JAVA_TRAP_DIR was not set");
}
String sourceArchiveVar = Env.systemEnv().getFirstNonEmpty("CODEQL_EXTRACTOR_JAVA_SOURCE_ARCHIVE_DIR",
Var.SOURCE_ARCHIVE.name());
String sourceArchiveVar = Env.systemEnv().get("CODEQL_EXTRACTOR_JAVA_SOURCE_ARCHIVE_DIR");
if (sourceArchiveVar == null) {
throw new ResourceError("CODEQL_EXTRACTOR_JAVA_SOURCE_ARCHIVE_DIR was not set");
}

View File

@@ -256,8 +256,6 @@ public class Env {
*/
ODASA_SRC,
ODASA_DB,
TRAP_FOLDER,
SOURCE_ARCHIVE,
ODASA_OUTPUT,
ODASA_SUBPROJECT_THREADS,

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added flow through some methods of the class `java.net.URL` by ensuring that the fields of a URL are tainted.

View File

@@ -0,0 +1,4 @@
---
category: breaking
---
* The Java and Kotlin extractors no longer support the `SOURCE_ARCHIVE` and `TRAP_FOLDER` legacy environment variable.

View File

@@ -3,7 +3,9 @@ extensions:
pack: codeql/java-all
extensible: sinkModel
data:
# Note these sinks do not use the sink kind `regex-use[0]` because they should be considered as sinks for regex injection but not polynomial ReDoS.
# Note these sinks do not use the sink kind `regex-use[0]` because the regex injection query needs to select them separately from
# other `regex-use[0]` sinks in order to avoid FPs. As a result, these sinks are currently not used in the polynomial ReDoS query.
# TODO: refactor the `regex-use%` sink kind so that the polynomial ReDoS query can also use these sinks.
- ["org.apache.commons.lang3", "RegExUtils", False, "removeAll", "(String,String)", "", "Argument[1]", "regex-use", "manual"]
- ["org.apache.commons.lang3", "RegExUtils", False, "removeFirst", "(String,String)", "", "Argument[1]", "regex-use", "manual"]
- ["org.apache.commons.lang3", "RegExUtils", False, "removePattern", "(String,String)", "", "Argument[1]", "regex-use", "manual"]

View File

@@ -22,6 +22,7 @@ private module Frameworks {
private import semmle.code.java.frameworks.IoJsonWebToken
private import semmle.code.java.frameworks.jackson.JacksonSerializability
private import semmle.code.java.frameworks.InputStream
private import semmle.code.java.frameworks.Networking
private import semmle.code.java.frameworks.Properties
private import semmle.code.java.frameworks.Protobuf
private import semmle.code.java.frameworks.ThreadLocal

View File

@@ -356,8 +356,12 @@ RefType getErasedRepr(Type t) {
t instanceof NullType and result instanceof TypeObject
}
class DataFlowType extends SrcRefType {
final private class SrcRefTypeFinal = SrcRefType;
class DataFlowType extends SrcRefTypeFinal {
DataFlowType() { this = getErasedRepr(_) }
string toString() { result = ppReprType(this) }
}
pragma[nomagic]
@@ -371,7 +375,7 @@ DataFlowType getNodeType(Node n) {
}
/** Gets a string representation of a type returned by `getErasedRepr`. */
string ppReprType(DataFlowType t) {
private string ppReprType(SrcRefType t) {
if t.(BoxedType).getPrimitiveType().getName() = "double"
then result = "Number"
else result = t.toString()

View File

@@ -3,6 +3,8 @@
*/
import semmle.code.java.Type
private import semmle.code.java.dataflow.DataFlow
private import semmle.code.java.dataflow.FlowSteps
/** The type `java.net.URLConnection`. */
class TypeUrlConnection extends RefType {
@@ -24,6 +26,11 @@ class TypeUrl extends RefType {
TypeUrl() { this.hasQualifiedName("java.net", "URL") }
}
/** Specifies that if a `URL` is tainted, then so are its synthetic fields. */
private class UrlFieldsInheritTaint extends DataFlow::SyntheticFieldContent, TaintInheritingContent {
UrlFieldsInheritTaint() { this.getField().matches("java.net.URL.%") }
}
/** The type `java.net.URLDecoder`. */
class TypeUrlDecoder extends RefType {
TypeUrlDecoder() { this.hasQualifiedName("java.net", "URLDecoder") }

View File

@@ -20,8 +20,10 @@ private class ExploitableStringLiteral extends StringLiteral {
* if no such argument exists.
*
* Note that `regex-use` is deliberately not a possible value for `kind` here,
* as it is used for regular expression injection sinks that should not be used
* as polynomial ReDoS sinks.
* as it is used for regular expression injection sinks that need to be selected
* separately from existing `regex-use[0]` sinks.
* TODO: refactor the `regex-use%` sink kind so that the polynomial ReDoS query
* can also use the `regex-use` sinks.
*/
private predicate regexSinkKindInfo(string kind, boolean full, int strArg) {
sinkModel(_, _, _, _, _, _, _, kind, _, _) and

View File

@@ -0,0 +1,18 @@
/**
* @kind test-postprocess
*/
import codeql.dataflow.test.ProvenancePathGraph
import semmle.code.java.dataflow.ExternalFlow
external predicate queryResults(string relation, int row, int column, string data);
external predicate queryRelations(string relation);
query predicate resultRelations(string relation) { queryRelations(relation) }
module Res = TranslateProvenanceResults<interpretModelForTest/2, queryResults/4>;
from string relation, int row, int column, string data
where Res::results(relation, row, column, data)
select relation, row, column, data

View File

@@ -90,6 +90,14 @@ public class Test {
out = in.toURL();
sink(out); // $ hasTaintFlow
}
{
// manual test for `URI.toURL().getPath()`; checks that if a `URL` is tainted, then so are its synthetic fields
// java.net;URL;False;getPath;();;Argument[this].SyntheticField[java.net.URL.path];ReturnValue;taint;ai-manual
URL out = null;
URI in = (URI) source();
out = in.toURL();
sink(out.getPath()); // $ hasTaintFlow
}
{
// "java.net;URL;false;URL;(String);;Argument[0];Argument[this];taint;manual"
URL out = null;
@@ -97,6 +105,14 @@ public class Test {
out = new URL(in);
sink(out); // $ hasTaintFlow
}
{
// manual test for `URL(String).getPath()`; checks that if a `URL` is tainted, then so are its synthetic fields
// java.net;URL;False;getPath;();;Argument[this].SyntheticField[java.net.URL.path];ReturnValue;taint;ai-manual
URL out = null;
String in = (String) source();
out = new URL(in);
sink(out.getPath()); // $ hasTaintFlow
}
{
// "java.net;URL;false;URL;(URL,String);;Argument[0];Argument[this];taint;ai-generated"
URL out = null;

View File

@@ -1,23 +1,33 @@
#select
| Test.java:7:44:7:69 | ... + ... | Test.java:57:27:57:39 | args : String[] | Test.java:7:25:7:70 | new ..[] { .. } | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value |
| Test.java:10:29:10:74 | new String[] | Test.java:57:27:57:39 | args : String[] | Test.java:10:29:10:74 | new String[] | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value |
| Test.java:18:29:18:31 | cmd | Test.java:57:27:57:39 | args : String[] | Test.java:18:29:18:31 | cmd | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value |
| Test.java:24:29:24:32 | cmd1 | Test.java:57:27:57:39 | args : String[] | Test.java:24:29:24:32 | cmd1 | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value |
| Test.java:29:44:29:64 | ... + ... | Test.java:57:27:57:39 | args : String[] | Test.java:29:25:29:65 | new ..[] { .. } | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value |
edges
| Test.java:6:35:6:44 | arg : String | Test.java:7:44:7:69 | ... + ... : String | provenance | |
| Test.java:6:35:6:44 | arg : String | Test.java:10:61:10:73 | ... + ... : String | provenance | |
| Test.java:6:35:6:44 | arg : String | Test.java:16:13:16:25 | ... + ... : String | provenance | |
| Test.java:6:35:6:44 | arg : String | Test.java:22:15:22:27 | ... + ... : String | provenance | |
| Test.java:7:25:7:70 | new ..[] { .. } : String[] [[]] : String | Test.java:7:25:7:70 | new ..[] { .. } | provenance | Sink:MaD:42682 |
| Test.java:7:25:7:70 | new ..[] { .. } : String[] [[]] : String | Test.java:7:25:7:70 | new ..[] { .. } | provenance | Sink:MaD:2 |
| Test.java:7:44:7:69 | ... + ... : String | Test.java:7:25:7:70 | new ..[] { .. } : String[] [[]] : String | provenance | |
| Test.java:10:29:10:74 | {...} : String[] [[]] : String | Test.java:10:29:10:74 | new String[] | provenance | Sink:MaD:42682 |
| Test.java:10:29:10:74 | {...} : String[] [[]] : String | Test.java:10:29:10:74 | new String[] | provenance | Sink:MaD:2 |
| Test.java:10:61:10:73 | ... + ... : String | Test.java:10:29:10:74 | {...} : String[] [[]] : String | provenance | |
| Test.java:16:5:16:7 | cmd [post update] : ArrayList [<element>] : String | Test.java:18:29:18:31 | cmd | provenance | Sink:MaD:42681 |
| Test.java:16:13:16:25 | ... + ... : String | Test.java:16:5:16:7 | cmd [post update] : ArrayList [<element>] : String | provenance | MaD:43744 |
| Test.java:22:5:22:8 | cmd1 [post update] : String[] [[]] : String | Test.java:24:29:24:32 | cmd1 | provenance | Sink:MaD:42682 |
| Test.java:16:5:16:7 | cmd [post update] : ArrayList [<element>] : String | Test.java:18:29:18:31 | cmd | provenance | Sink:MaD:1 |
| Test.java:16:13:16:25 | ... + ... : String | Test.java:16:5:16:7 | cmd [post update] : ArrayList [<element>] : String | provenance | MaD:3 |
| Test.java:22:5:22:8 | cmd1 [post update] : String[] [[]] : String | Test.java:24:29:24:32 | cmd1 | provenance | Sink:MaD:2 |
| Test.java:22:15:22:27 | ... + ... : String | Test.java:22:5:22:8 | cmd1 [post update] : String[] [[]] : String | provenance | |
| Test.java:28:38:28:47 | arg : String | Test.java:29:44:29:64 | ... + ... : String | provenance | |
| Test.java:29:25:29:65 | new ..[] { .. } : String[] [[]] : String | Test.java:29:25:29:65 | new ..[] { .. } | provenance | Sink:MaD:42682 |
| Test.java:29:25:29:65 | new ..[] { .. } : String[] [[]] : String | Test.java:29:25:29:65 | new ..[] { .. } | provenance | Sink:MaD:2 |
| Test.java:29:44:29:64 | ... + ... : String | Test.java:29:25:29:65 | new ..[] { .. } : String[] [[]] : String | provenance | |
| Test.java:57:27:57:39 | args : String[] | Test.java:60:20:60:22 | arg : String | provenance | |
| Test.java:57:27:57:39 | args : String[] | Test.java:61:23:61:25 | arg : String | provenance | |
| Test.java:60:20:60:22 | arg : String | Test.java:6:35:6:44 | arg : String | provenance | |
| Test.java:61:23:61:25 | arg : String | Test.java:28:38:28:47 | arg : String | provenance | |
models
| 1 | Sink: java.lang; ProcessBuilder; false; ProcessBuilder; (List); ; Argument[0]; command-injection; ai-manual |
| 2 | Sink: java.lang; ProcessBuilder; false; ProcessBuilder; (String[]); ; Argument[0]; command-injection; ai-manual |
| 3 | Summary: java.util; Collection; true; add; ; ; Argument[0]; Argument[this].Element; value; manual |
nodes
| Test.java:6:35:6:44 | arg : String | semmle.label | arg : String |
| Test.java:7:25:7:70 | new ..[] { .. } | semmle.label | new ..[] { .. } |
@@ -40,9 +50,3 @@ nodes
| Test.java:60:20:60:22 | arg : String | semmle.label | arg : String |
| Test.java:61:23:61:25 | arg : String | semmle.label | arg : String |
subpaths
#select
| Test.java:7:44:7:69 | ... + ... | Test.java:57:27:57:39 | args : String[] | Test.java:7:25:7:70 | new ..[] { .. } | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value |
| Test.java:10:29:10:74 | new String[] | Test.java:57:27:57:39 | args : String[] | Test.java:10:29:10:74 | new String[] | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value |
| Test.java:18:29:18:31 | cmd | Test.java:57:27:57:39 | args : String[] | Test.java:18:29:18:31 | cmd | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value |
| Test.java:24:29:24:32 | cmd1 | Test.java:57:27:57:39 | args : String[] | Test.java:24:29:24:32 | cmd1 | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value |
| Test.java:29:44:29:64 | ... + ... | Test.java:57:27:57:39 | args : String[] | Test.java:29:25:29:65 | new ..[] { .. } | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value |

View File

@@ -1 +1,2 @@
Security/CWE/CWE-078/ExecTainted.ql
query: Security/CWE/CWE-078/ExecTainted.ql
postprocess: TestUtilities/PrettyPrintModels.ql

View File

@@ -1,14 +0,0 @@
/**
* @kind path-problem
*/
import java
import semmle.code.java.security.HttpsUrlsQuery
import codeql.dataflow.test.ProvenancePathGraph
import semmle.code.java.dataflow.ExternalFlow
import ShowProvenance<interpretModelForTest/2, HttpStringToUrlOpenMethodFlow::PathNode, HttpStringToUrlOpenMethodFlow::PathGraph>
from HttpStringToUrlOpenMethodFlow::PathNode source, HttpStringToUrlOpenMethodFlow::PathNode sink
where HttpStringToUrlOpenMethodFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "URL may have been constructed with HTTP protocol, using $@.",
source.getNode(), "this HTTP URL"

View File

@@ -0,0 +1,2 @@
query: Security/CWE/CWE-319/HttpsUrls.ql
postprocess: TestUtilities/PrettyPrintModels.ql

View File

@@ -534,7 +534,7 @@ newtype TDataFlowType = TAnyFlow()
class DataFlowType extends TDataFlowType {
/** Gets a textual representation of this element. */
string toString() { result = "DataFlowType" }
string toString() { result = "" }
}
/** A node that performs a type cast. */
@@ -578,9 +578,6 @@ DataFlowType getNodeType(Node node) {
exists(node)
}
/** Gets a string representation of a type returned by `getErasedRepr`. */
string ppReprType(DataFlowType t) { none() }
//--------
// Extra flow
//--------

View File

@@ -0,0 +1,48 @@
/**
* Provides default sources, sinks and sanitizers for detecting
* "cookie injection"
* vulnerabilities, as well as extension points for adding your own.
*/
private import python
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts
private import semmle.python.dataflow.new.RemoteFlowSources
/**
* Provides default sources, sinks and sanitizers for detecting
* "cookie injection"
* vulnerabilities, as well as extension points for adding your own.
*/
module CookieInjection {
/**
* A data flow source for "cookie injection" vulnerabilities.
*/
abstract class Source extends DataFlow::Node { }
/**
* A data flow sink for "cookie injection" vulnerabilities.
*/
abstract class Sink extends DataFlow::Node { }
/**
* A sanitizer for "cookie injection" vulnerabilities.
*/
abstract class Sanitizer extends DataFlow::Node { }
/**
* A source of remote user input, considered as a flow source.
*/
class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { }
/**
* A write to a cookie, considered as a sink.
*/
class CookieWriteSink extends Sink {
CookieWriteSink() {
exists(Http::Server::CookieWrite cw |
this = [cw.getNameArg(), cw.getValueArg(), cw.getHeaderArg()]
)
}
}
}

View File

@@ -0,0 +1,26 @@
/**
* Provides a taint-tracking configuration for detecting "cookie injection" vulnerabilities.
*
* Note, for performance reasons: only import this file if
* `CookieInjectionFlow` is needed, otherwise
* `CookieInjectionCustomizations` should be imported instead.
*/
private import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.TaintTracking
import CookieInjectionCustomizations::CookieInjection
/**
* A taint-tracking configuration for detecting "cookie injection" vulnerabilities.
*/
module CookieInjectionConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
/** Global taint-tracking for detecting "cookie injection" vulnerabilities. */
module CookieInjectionFlow = TaintTracking::Global<CookieInjectionConfig>;

View File

@@ -0,0 +1,27 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Constructing cookies from user input can allow an attacker to control a user's cookie.
This may lead to a session fixation attack. Additionally, client code may not expect a cookie to contain attacker-controlled data, and fail to sanitize it for common vulnerabilities such as Cross Site Scripting (XSS).
An attacker manipulating the raw cookie header may additionally be able to set cookie attributes such as <code>HttpOnly</code> to insecure values.
</p>
</overview>
<recommendation>
<p>Do not use raw user input to construct cookies.</p>
</recommendation>
<example>
<p>In the following cases, a cookie is constructed for a Flask response using user input. The first uses <code>set_cookie</code>,
and the second sets a cookie's raw value through the <code>set-cookie</code> header.</p>
<sample src="examples/CookieInjection.py" />
</example>
<references>
<li>Wikipedia - <a href="https://en.wikipedia.org/wiki/Session_fixation">Session Fixation</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,20 @@
/**
* @name Construction of a cookie using user-supplied input.
* @description Constructing cookies from user input may allow an attacker to perform a Cookie Poisoning attack.
* @kind path-problem
* @problem.severity warning
* @precision high
* @security-severity 5.0
* @id py/cookie-injection
* @tags security
* external/cwe/cwe-20
*/
import python
import semmle.python.security.dataflow.CookieInjectionQuery
import CookieInjectionFlow::PathGraph
from CookieInjectionFlow::PathNode source, CookieInjectionFlow::PathNode sink
where CookieInjectionFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "Cookie is constructed from a $@.", source.getNode(),
"user-supplied input"

View File

@@ -2,15 +2,15 @@ from flask import request, make_response
@app.route("/1")
def true():
def set_cookie():
resp = make_response()
resp.set_cookie(request.args["name"],
resp.set_cookie(request.args["name"], # BAD: User input is used to set the cookie's name and value
value=request.args["name"])
return resp
@app.route("/2")
def flask_make_response():
resp = make_response("hello")
resp.headers['Set-Cookie'] = f"{request.args['name']}={request.args['name']};"
def set_cookie_header():
resp = make_response()
resp.headers['Set-Cookie'] = f"{request.args['name']}={request.args['name']};" # BAD: User input is used to set the raw cookie header.
return resp

View File

@@ -0,0 +1,4 @@
---
category: newQuery
---
* The `py/cookie-injection` query, originally contributed to the experimental query pack by @jorgectf, has been promoted to the main query pack. This query finds instances of cookies being constructed from user input.

View File

@@ -1,28 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Constructing cookies from user input may allow an attacker to perform a Cookie Poisoning attack.
It is possible, however, to perform other parameter-like attacks through cookie poisoning techniques,
such as SQL Injection, Directory Traversal, or Stealth Commanding, etc. Additionally,
cookie injection may relate to attempts to perform Access of Administrative Interface.
</p>
</overview>
<recommendation>
<p>Do not use raw user input to construct cookies.</p>
</recommendation>
<example>
<p>This example shows two ways of adding a cookie to a Flask response. The first way uses <code>set_cookie</code>'s
and the second sets a cookie's raw value through a header, both using user-supplied input.</p>
<sample src="CookieInjection.py" />
</example>
<references>
<li>Imperva: <a href="https://docs.imperva.com/bundle/on-premises-knowledgebase-reference-guide/page/cookie_injection.htm">Cookie injection</a>.</li>
</references>
</qhelp>

View File

@@ -1,27 +0,0 @@
/**
* @name Construction of a cookie using user-supplied input.
* @description Constructing cookies from user input may allow an attacker to perform a Cookie Poisoning attack.
* @kind path-problem
* @problem.severity error
* @id py/cookie-injection
* @tags security
* experimental
* external/cwe/cwe-614
*/
// determine precision above
import python
import semmle.python.dataflow.new.DataFlow
import experimental.semmle.python.Concepts
import experimental.semmle.python.CookieHeader
import experimental.semmle.python.security.injection.CookieInjection
import CookieInjectionFlow::PathGraph
from CookieInjectionFlow::PathNode source, CookieInjectionFlow::PathNode sink, string insecure
where
CookieInjectionFlow::flowPath(source, sink) and
if exists(sink.getNode().(CookieSink))
then insecure = ",and its " + sink.getNode().(CookieSink).getFlag() + " flag is not properly set."
else insecure = "."
select sink.getNode(), source, sink, "Cookie is constructed from a $@" + insecure, source.getNode(),
"user-supplied input"

View File

@@ -1,41 +0,0 @@
import python
import experimental.semmle.python.Concepts
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.TaintTracking
import semmle.python.dataflow.new.RemoteFlowSources
class CookieSink extends DataFlow::Node {
string flag;
CookieSink() {
exists(Cookie cookie |
this in [cookie.getNameArg(), cookie.getValueArg()] and
(
not cookie.isSecure() and
flag = "secure"
or
not cookie.isHttpOnly() and
flag = "httponly"
or
not cookie.isSameSite() and
flag = "samesite"
)
)
}
string getFlag() { result = flag }
}
/**
* A taint-tracking configuration for detecting Cookie injections.
*/
private module CookieInjectionConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
predicate isSink(DataFlow::Node sink) {
exists(Cookie c | sink in [c.getNameArg(), c.getValueArg()])
}
}
/** Global taint-tracking for detecting "Cookie injections" vulnerabilities. */
module CookieInjectionFlow = TaintTracking::Global<CookieInjectionConfig>;

View File

@@ -1,51 +0,0 @@
edges
| django_bad.py:27:33:27:67 | ControlFlowNode for Attribute() | django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | provenance | |
| django_bad.py:27:71:27:106 | ControlFlowNode for Attribute() | django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | provenance | |
| flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | flask_bad.py:1:26:1:32 | ControlFlowNode for request | provenance | |
| flask_bad.py:1:26:1:32 | ControlFlowNode for request | flask_bad.py:24:21:24:27 | ControlFlowNode for request | provenance | |
| flask_bad.py:1:26:1:32 | ControlFlowNode for request | flask_bad.py:24:49:24:55 | ControlFlowNode for request | provenance | |
| flask_bad.py:1:26:1:32 | ControlFlowNode for request | flask_bad.py:32:37:32:43 | ControlFlowNode for request | provenance | |
| flask_bad.py:1:26:1:32 | ControlFlowNode for request | flask_bad.py:32:60:32:66 | ControlFlowNode for request | provenance | |
| flask_bad.py:24:21:24:27 | ControlFlowNode for request | flask_bad.py:24:21:24:40 | ControlFlowNode for Subscript | provenance | AdditionalTaintStep |
| flask_bad.py:24:21:24:27 | ControlFlowNode for request | flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | provenance | AdditionalTaintStep |
| flask_bad.py:24:49:24:55 | ControlFlowNode for request | flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | provenance | AdditionalTaintStep |
| flask_bad.py:32:37:32:43 | ControlFlowNode for request | flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | provenance | AdditionalTaintStep |
| flask_bad.py:32:60:32:66 | ControlFlowNode for request | flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | provenance | AdditionalTaintStep |
nodes
| django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | semmle.label | ControlFlowNode for Fstring |
| django_bad.py:27:33:27:67 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| django_bad.py:27:71:27:106 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember |
| flask_bad.py:1:26:1:32 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| flask_bad.py:24:21:24:27 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| flask_bad.py:24:21:24:40 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| flask_bad.py:24:49:24:55 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | semmle.label | ControlFlowNode for Fstring |
| flask_bad.py:32:37:32:43 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| flask_bad.py:32:60:32:66 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
subpaths
#select
| django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | Cookie is constructed from a $@,and its httponly flag is not properly set. | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | user-supplied input |
| django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | Cookie is constructed from a $@,and its samesite flag is not properly set. | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | user-supplied input |
| django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | Cookie is constructed from a $@,and its secure flag is not properly set. | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | user-supplied input |
| django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | Cookie is constructed from a $@,and its httponly flag is not properly set. | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | user-supplied input |
| django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | Cookie is constructed from a $@,and its samesite flag is not properly set. | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | user-supplied input |
| django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | Cookie is constructed from a $@,and its secure flag is not properly set. | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | user-supplied input |
| django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | django_bad.py:27:33:27:67 | ControlFlowNode for Attribute() | django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | Cookie is constructed from a $@,and its httponly flag is not properly set. | django_bad.py:27:33:27:67 | ControlFlowNode for Attribute() | user-supplied input |
| django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | django_bad.py:27:33:27:67 | ControlFlowNode for Attribute() | django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | Cookie is constructed from a $@,and its samesite flag is not properly set. | django_bad.py:27:33:27:67 | ControlFlowNode for Attribute() | user-supplied input |
| django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | django_bad.py:27:33:27:67 | ControlFlowNode for Attribute() | django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | Cookie is constructed from a $@,and its secure flag is not properly set. | django_bad.py:27:33:27:67 | ControlFlowNode for Attribute() | user-supplied input |
| django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | django_bad.py:27:71:27:106 | ControlFlowNode for Attribute() | django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | Cookie is constructed from a $@,and its httponly flag is not properly set. | django_bad.py:27:71:27:106 | ControlFlowNode for Attribute() | user-supplied input |
| django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | django_bad.py:27:71:27:106 | ControlFlowNode for Attribute() | django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | Cookie is constructed from a $@,and its samesite flag is not properly set. | django_bad.py:27:71:27:106 | ControlFlowNode for Attribute() | user-supplied input |
| django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | django_bad.py:27:71:27:106 | ControlFlowNode for Attribute() | django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | Cookie is constructed from a $@,and its secure flag is not properly set. | django_bad.py:27:71:27:106 | ControlFlowNode for Attribute() | user-supplied input |
| flask_bad.py:24:21:24:40 | ControlFlowNode for Subscript | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | flask_bad.py:24:21:24:40 | ControlFlowNode for Subscript | Cookie is constructed from a $@,and its httponly flag is not properly set. | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | user-supplied input |
| flask_bad.py:24:21:24:40 | ControlFlowNode for Subscript | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | flask_bad.py:24:21:24:40 | ControlFlowNode for Subscript | Cookie is constructed from a $@,and its samesite flag is not properly set. | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | user-supplied input |
| flask_bad.py:24:21:24:40 | ControlFlowNode for Subscript | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | flask_bad.py:24:21:24:40 | ControlFlowNode for Subscript | Cookie is constructed from a $@,and its secure flag is not properly set. | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | user-supplied input |
| flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | Cookie is constructed from a $@,and its httponly flag is not properly set. | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | user-supplied input |
| flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | Cookie is constructed from a $@,and its samesite flag is not properly set. | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | user-supplied input |
| flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | Cookie is constructed from a $@,and its secure flag is not properly set. | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | user-supplied input |
| flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | Cookie is constructed from a $@,and its httponly flag is not properly set. | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | user-supplied input |
| flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | Cookie is constructed from a $@,and its samesite flag is not properly set. | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | user-supplied input |
| flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | Cookie is constructed from a $@,and its secure flag is not properly set. | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | user-supplied input |

View File

@@ -1 +0,0 @@
experimental/Security/CWE-614/CookieInjection.ql

View File

@@ -0,0 +1,4 @@
argumentToEnsureNotTaintedNotMarkedAsSpurious
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
testFailures
failures

View File

@@ -0,0 +1,2 @@
import experimental.meta.InlineTaintTest
import MakeInlineTaintTest<TestTaintTrackingConfig>

View File

@@ -0,0 +1,11 @@
def impossible_flow(cond: bool):
TAINTED_STRING = "ts"
x = (TAINTED_STRING, 42) if cond else "SAFE"
if isinstance(x, str):
# tainted-flow to here is impossible, replicated from path-flow seen in a real
# repo.
ensure_not_tainted(x) # $ SPURIOUS: tainted
else:
ensure_tainted(x) # $ tainted
ensure_tainted(x[0]) # $ tainted

View File

@@ -0,0 +1,28 @@
edges
| django_tests.py:4:25:4:31 | ControlFlowNode for request | django_tests.py:6:21:6:31 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
| django_tests.py:4:25:4:31 | ControlFlowNode for request | django_tests.py:7:21:7:31 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
| django_tests.py:6:21:6:31 | ControlFlowNode for Attribute | django_tests.py:6:21:6:43 | ControlFlowNode for Attribute() | provenance | dict.get |
| django_tests.py:7:21:7:31 | ControlFlowNode for Attribute | django_tests.py:7:21:7:44 | ControlFlowNode for Attribute() | provenance | dict.get |
| django_tests.py:11:26:11:32 | ControlFlowNode for request | django_tests.py:13:33:13:43 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
| django_tests.py:11:26:11:32 | ControlFlowNode for request | django_tests.py:13:59:13:69 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
| django_tests.py:13:33:13:43 | ControlFlowNode for Attribute | django_tests.py:13:33:13:55 | ControlFlowNode for Attribute() | provenance | dict.get |
| django_tests.py:13:33:13:55 | ControlFlowNode for Attribute() | django_tests.py:13:30:13:100 | ControlFlowNode for Fstring | provenance | |
| django_tests.py:13:59:13:69 | ControlFlowNode for Attribute | django_tests.py:13:59:13:82 | ControlFlowNode for Attribute() | provenance | dict.get |
| django_tests.py:13:59:13:82 | ControlFlowNode for Attribute() | django_tests.py:13:30:13:100 | ControlFlowNode for Fstring | provenance | |
nodes
| django_tests.py:4:25:4:31 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| django_tests.py:6:21:6:31 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| django_tests.py:6:21:6:43 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| django_tests.py:7:21:7:31 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| django_tests.py:7:21:7:44 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| django_tests.py:11:26:11:32 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| django_tests.py:13:30:13:100 | ControlFlowNode for Fstring | semmle.label | ControlFlowNode for Fstring |
| django_tests.py:13:33:13:43 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| django_tests.py:13:33:13:55 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| django_tests.py:13:59:13:69 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| django_tests.py:13:59:13:82 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
subpaths
#select
| django_tests.py:6:21:6:43 | ControlFlowNode for Attribute() | django_tests.py:4:25:4:31 | ControlFlowNode for request | django_tests.py:6:21:6:43 | ControlFlowNode for Attribute() | Cookie is constructed from a $@. | django_tests.py:4:25:4:31 | ControlFlowNode for request | user-supplied input |
| django_tests.py:7:21:7:44 | ControlFlowNode for Attribute() | django_tests.py:4:25:4:31 | ControlFlowNode for request | django_tests.py:7:21:7:44 | ControlFlowNode for Attribute() | Cookie is constructed from a $@. | django_tests.py:4:25:4:31 | ControlFlowNode for request | user-supplied input |
| django_tests.py:13:30:13:100 | ControlFlowNode for Fstring | django_tests.py:11:26:11:32 | ControlFlowNode for request | django_tests.py:13:30:13:100 | ControlFlowNode for Fstring | Cookie is constructed from a $@. | django_tests.py:11:26:11:32 | ControlFlowNode for request | user-supplied input |

View File

@@ -0,0 +1 @@
Security/CWE-020/CookieInjection.ql

View File

@@ -0,0 +1,20 @@
import django.http
from django.urls import path
def django_response_bad(request):
resp = django.http.HttpResponse()
resp.set_cookie(request.GET.get("name"), # BAD: Cookie is constructed from user input
request.GET.get("value"))
return resp
def django_response_bad2(request):
response = django.http.HttpResponse()
response['Set-Cookie'] = f"{request.GET.get('name')}={request.GET.get('value')}; SameSite=None;" # BAD: Cookie header is constructed from user input.
return response
# fake setup, you can't actually run this
urlpatterns = [
path("response_bad", django_response_bad),
path("response_bd2", django_response_bad2)
]

View File

@@ -2077,9 +2077,6 @@ DataFlowType getNodeType(Node n) {
result = TUnknownDataFlowType()
}
/** Gets a string representation of a `DataFlowType`. */
string ppReprType(DataFlowType t) { none() }
pragma[inline]
private predicate compatibleTypesNonSymRefl(DataFlowType t1, DataFlowType t2) {
t1 != TUnknownDataFlowType() and

View File

@@ -0,0 +1,238 @@
/**
* Provides default sources, sinks and sanitizers for detecting
* "use of a broken or weak cryptographic hashing algorithm on sensitive data"
* vulnerabilities, as well as extension points for adding your own.
*/
private import ruby
private import codeql.ruby.Concepts
private import codeql.ruby.security.SensitiveActions
private import codeql.ruby.dataflow.BarrierGuards
private import codeql.ruby.dataflow.SSA
private module SensitiveDataSources {
/**
* A data flow source of sensitive data, such as secrets, certificates, or passwords.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `SensitiveDataSource::Range` instead.
*/
class SensitiveDataSource extends DataFlow::Node instanceof SensitiveDataSource::Range {
/**
* Gets the classification of the sensitive data.
*/
SensitiveDataClassification getClassification() { result = super.getClassification() }
}
/** Provides a class for modeling new sources of sensitive data, such as secrets, certificates, or passwords. */
module SensitiveDataSource {
/**
* A data flow source of sensitive data, such as secrets, certificates, or passwords.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `SensitiveDataSource` instead.
*/
abstract class Range extends DataFlow::Node {
/**
* Gets the classification of the sensitive data.
*/
abstract SensitiveDataClassification getClassification();
}
}
/**
* A call to a method that may return sensitive data.
*/
class SensitiveMethodCall extends SensitiveDataSource::Range instanceof SensitiveCall {
override SensitiveDataClassification getClassification() {
result = SensitiveCall.super.getClassification()
}
}
/**
* An assignment to a variable that may contain sensitive data.
*/
class SensitiveVariableAssignment extends SensitiveDataSource::Range, DataFlow::SsaDefinitionNode {
SensitiveNode sensitiveNode;
SensitiveVariableAssignment() {
this.getDefinition().(Ssa::WriteDefinition).getWriteAccess() = sensitiveNode.asExpr()
}
override SensitiveDataClassification getClassification() {
result = sensitiveNode.getClassification()
}
}
/**
* A read from a hash value that may return sensitive data.
*/
class SensitiveHashValueAccess extends SensitiveDataSource::Range instanceof SensitiveNode {
SensitiveHashValueAccess() {
this.asExpr() instanceof Cfg::CfgNodes::ExprNodes::ElementReferenceCfgNode
}
override SensitiveDataClassification getClassification() {
result = SensitiveNode.super.getClassification()
}
}
/**
* A parameter node that may contain sensitive data.
*/
class SensitiveParameter extends SensitiveDataSource::Range, DataFlow::ParameterNode instanceof SensitiveNode
{
override SensitiveDataClassification getClassification() {
result = SensitiveNode.super.getClassification()
}
}
}
/**
* Provides default sources, sinks and sanitizers for detecting
* "use of a broken or weak cryptographic hashing algorithm on sensitive data"
* vulnerabilities on sensitive data that does NOT require computationally expensive
* hashing, as well as extension points for adding your own.
*
* Also see the `ComputationallyExpensiveHashFunction` module.
*/
module NormalHashFunction {
/**
* A data flow source for "use of a broken or weak cryptographic hashing algorithm on
* sensitive data" vulnerabilities.
*/
abstract class Source extends DataFlow::Node {
Source() { not this instanceof ComputationallyExpensiveHashFunction::Source }
/** Gets the classification of the sensitive data. */
abstract string getClassification();
}
/**
* A data flow sink for "use of a broken or weak cryptographic hashing algorithm on
* sensitive data" vulnerabilities.
*/
abstract class Sink extends DataFlow::Node {
/**
* Gets the name of the weak hashing algorithm.
*/
abstract string getAlgorithmName();
}
/**
* A sanitizer for "use of a broken or weak cryptographic hashing algorithm on
* sensitive data" vulnerabilities.
*/
abstract class Sanitizer extends DataFlow::Node { }
/**
* A source of sensitive data, considered as a flow source.
*/
class SensitiveDataSourceAsSource extends Source instanceof SensitiveDataSources::SensitiveDataSource
{
override SensitiveDataClassification getClassification() {
result = SensitiveDataSources::SensitiveDataSource.super.getClassification()
}
}
/** The input to a hashing operation using a weak algorithm, considered as a flow sink. */
class WeakHashingOperationInputSink extends Sink {
Cryptography::HashingAlgorithm algorithm;
WeakHashingOperationInputSink() {
exists(Cryptography::CryptographicOperation operation |
algorithm = operation.getAlgorithm() and
algorithm.isWeak() and
this = operation.getAnInput()
)
}
override string getAlgorithmName() { result = algorithm.getName() }
}
}
/**
* Provides default sources, sinks and sanitizers for detecting
* "use of a broken or weak cryptographic hashing algorithm on sensitive data"
* vulnerabilities on sensitive data that DOES require computationally expensive
* hashing, as well as extension points for adding your own.
*
* Also see the `NormalHashFunction` module.
*/
module ComputationallyExpensiveHashFunction {
/**
* A data flow source of sensitive data that requires computationally expensive
* hashing for "use of a broken or weak cryptographic hashing algorithm on sensitive
* data" vulnerabilities.
*/
abstract class Source extends DataFlow::Node {
/** Gets the classification of the sensitive data. */
abstract string getClassification();
}
/**
* A data flow sink for sensitive data that requires computationally expensive
* hashing for "use of a broken or weak cryptographic hashing algorithm on sensitive
* data" vulnerabilities.
*/
abstract class Sink extends DataFlow::Node {
/**
* Gets the name of the weak hashing algorithm.
*/
abstract string getAlgorithmName();
/**
* Holds if this sink is for a computationally expensive hash function (meaning that
* hash function is just weak in some other regard.
*/
abstract predicate isComputationallyExpensive();
}
/**
* A sanitizer of sensitive data that requires computationally expensive
* hashing for "use of a broken or weak cryptographic hashing
* algorithm on sensitive data" vulnerabilities.
*/
abstract class Sanitizer extends DataFlow::Node { }
/**
* A source of passwords, considered as a flow source.
*/
class PasswordSourceAsSource extends Source instanceof SensitiveDataSources::SensitiveDataSource {
PasswordSourceAsSource() {
this.(SensitiveDataSources::SensitiveDataSource).getClassification() =
SensitiveDataClassification::password()
}
override SensitiveDataClassification getClassification() {
result = SensitiveDataSources::SensitiveDataSource.super.getClassification()
}
}
/**
* The input to a password hashing operation using a weak algorithm, considered as a
* flow sink.
*/
class WeakPasswordHashingOperationInputSink extends Sink {
Cryptography::CryptographicAlgorithm algorithm;
WeakPasswordHashingOperationInputSink() {
(
algorithm instanceof Cryptography::PasswordHashingAlgorithm and
algorithm.isWeak()
or
algorithm instanceof Cryptography::HashingAlgorithm // Note that HashingAlgorithm and PasswordHashingAlgorithm are disjoint
) and
exists(Cryptography::CryptographicOperation operation |
algorithm = operation.getAlgorithm() and
this = operation.getAnInput()
)
}
override string getAlgorithmName() { result = algorithm.getName() }
override predicate isComputationallyExpensive() {
algorithm instanceof Cryptography::PasswordHashingAlgorithm
}
}
}

View File

@@ -0,0 +1,87 @@
/**
* Provides a taint-tracking configuration for detecting use of a broken or weak
* cryptographic hashing algorithm on sensitive data.
*
* Note, for performance reasons: only import this file if
* `WeakSensitiveDataHashing::Configuration` is needed, otherwise
* `WeakSensitiveDataHashingCustomizations` should be imported instead.
*/
private import ruby
private import codeql.ruby.Concepts
private import codeql.ruby.TaintTracking
private import codeql.ruby.dataflow.RemoteFlowSources
private import codeql.ruby.dataflow.BarrierGuards
private import codeql.ruby.security.SensitiveActions
/**
* Provides a taint-tracking configuration for detecting use of a broken or weak
* cryptographic hash function on sensitive data, that does NOT require a
* computationally expensive hash function.
*/
module NormalHashFunction {
import WeakSensitiveDataHashingCustomizations::NormalHashFunction
private module Config implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
/** Global taint-tracking for detecting "use of a broken or weak cryptographic hashing algorithm on sensitive data" vulnerabilities. */
module Flow = TaintTracking::Global<Config>;
}
/**
* Provides a taint-tracking configuration for detecting use of a broken or weak
* cryptographic hashing algorithm on passwords.
*
* Passwords has stricter requirements on the hashing algorithm used (must be
* computationally expensive to prevent brute-force attacks).
*/
module ComputationallyExpensiveHashFunction {
import WeakSensitiveDataHashingCustomizations::ComputationallyExpensiveHashFunction
/**
* Passwords has stricter requirements on the hashing algorithm used (must be
* computationally expensive to prevent brute-force attacks).
*/
private module Config implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
/** Global taint-tracking for detecting "use of a broken or weak cryptographic hashing algorithm on passwords" vulnerabilities. */
module Flow = TaintTracking::Global<Config>;
}
/**
* Global taint-tracking for detecting both variants of "use of a broken or weak
* cryptographic hashing algorithm on sensitive data" vulnerabilities.
*
* See convenience predicates `normalHashFunctionFlowPath` and
* `computationallyExpensiveHashFunctionFlowPath`.
*/
module WeakSensitiveDataHashingFlow =
DataFlow::MergePathGraph<NormalHashFunction::Flow::PathNode,
ComputationallyExpensiveHashFunction::Flow::PathNode, NormalHashFunction::Flow::PathGraph,
ComputationallyExpensiveHashFunction::Flow::PathGraph>;
/** Holds if data can flow from `source` to `sink` with `NormalHashFunction::Flow`. */
predicate normalHashFunctionFlowPath(
WeakSensitiveDataHashingFlow::PathNode source, WeakSensitiveDataHashingFlow::PathNode sink
) {
NormalHashFunction::Flow::flowPath(source.asPathNode1(), sink.asPathNode1())
}
/** Holds if data can flow from `source` to `sink` with `ComputationallyExpensiveHashFunction::Flow`. */
predicate computationallyExpensiveHashFunctionFlowPath(
WeakSensitiveDataHashingFlow::PathNode source, WeakSensitiveDataHashingFlow::PathNode sink
) {
ComputationallyExpensiveHashFunction::Flow::flowPath(source.asPathNode2(), sink.asPathNode2())
}

View File

@@ -0,0 +1,4 @@
---
category: newQuery
---
* Added a new query, `rb/weak-sensitive-data-hashing`, to detect cases where sensitive data is hashed using a weak cryptographic hashing algorithm.

View File

@@ -0,0 +1,104 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Using a broken or weak cryptographic hash function can leave data
vulnerable, and should not be used in security related code.
</p>
<p>
A strong cryptographic hash function should be resistant to:
</p>
<ul>
<li>
pre-image attacks: if you know a hash value <code>h(x)</code>,
you should not be able to easily find the input <code>x</code>.
</li>
<li>
collision attacks: if you know a hash value <code>h(x)</code>,
you should not be able to easily find a different input <code>y</code>
with the same hash value <code>h(x) = h(y)</code>.
</li>
</ul>
<p>
In cases with a limited input space, such as for passwords, the hash
function also needs to be computationally expensive to be resistant to
brute-force attacks. Passwords should also have an unique salt applied
before hashing, but that is not considered by this query.
</p>
<p>
As an example, both MD5 and SHA-1 are known to be vulnerable to collision attacks.
</p>
<p>
Since it's OK to use a weak cryptographic hash function in a non-security
context, this query only alerts when these are used to hash sensitive
data (such as passwords, certificates, usernames).
</p>
<p>
Use of broken or weak cryptographic algorithms that are not hashing algorithms, is
handled by the <code>rb/weak-cryptographic-algorithm</code> query.
</p>
</overview>
<recommendation>
<p>
Ensure that you use a strong, modern cryptographic hash function:
</p>
<ul>
<li>
such as Argon2, scrypt, bcrypt, or PBKDF2 for passwords and other data with limited input space.
</li>
<li>
such as SHA-2, or SHA-3 in other cases.
</li>
</ul>
</recommendation>
<example>
<p>
The following example shows two functions for checking whether the hash
of a certificate matches a known value -- to prevent tampering.
The first function uses MD5 that is known to be vulnerable to collision attacks.
The second function uses SHA-256 that is a strong cryptographic hashing function.
</p>
<sample src="examples/weak_certificate_hashing.rb" />
</example>
<example>
<p>
The following example shows two functions for hashing passwords.
The first function uses SHA-256 to hash passwords. Although SHA-256 is a
strong cryptographic hash function, it is not suitable for password
hashing since it is not computationally expensive.
</p>
<sample src="examples/weak_password_hashing_bad.rb" />
<p>
The second function uses Argon2 (through the <code>argon2</code>
gem), which is a strong password hashing algorithm (and
includes a per-password salt by default).
</p>
<sample src="examples/weak_password_hashing_good.rb" />
</example>
<references>
<li>OWASP: <a href="https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html">Password Storage Cheat Sheet</a></li>
</references>
</qhelp>

View File

@@ -0,0 +1,43 @@
/**
* @name Use of a broken or weak cryptographic hashing algorithm on sensitive data
* @description Using broken or weak cryptographic hashing algorithms can compromise security.
* @kind path-problem
* @problem.severity warning
* @security-severity 7.5
* @precision high
* @id rb/weak-sensitive-data-hashing
* @tags security
* external/cwe/cwe-327
* external/cwe/cwe-328
* external/cwe/cwe-916
*/
import ruby
import codeql.ruby.security.WeakSensitiveDataHashingQuery
import WeakSensitiveDataHashingFlow::PathGraph
from
WeakSensitiveDataHashingFlow::PathNode source, WeakSensitiveDataHashingFlow::PathNode sink,
string ending, string algorithmName, string classification
where
normalHashFunctionFlowPath(source, sink) and
algorithmName = sink.getNode().(NormalHashFunction::Sink).getAlgorithmName() and
classification = source.getNode().(NormalHashFunction::Source).getClassification() and
ending = "."
or
computationallyExpensiveHashFunctionFlowPath(source, sink) and
algorithmName = sink.getNode().(ComputationallyExpensiveHashFunction::Sink).getAlgorithmName() and
classification =
source.getNode().(ComputationallyExpensiveHashFunction::Source).getClassification() and
(
sink.getNode().(ComputationallyExpensiveHashFunction::Sink).isComputationallyExpensive() and
ending = "."
or
not sink.getNode().(ComputationallyExpensiveHashFunction::Sink).isComputationallyExpensive() and
ending =
" for " + classification +
" hashing, since it is not a computationally expensive hash function."
)
select sink.getNode(), source, sink,
"$@ is used in a hashing algorithm (" + algorithmName + ") that is insecure" + ending,
source.getNode(), "Sensitive data (" + classification + ")"

View File

@@ -0,0 +1,11 @@
require 'openssl'
def certificate_matches_known_hash_bad(certificate, known_hash)
hash = OpenSSL::Digest.new('SHA1').digest certificate
hash == known_hash
end
def certificate_matches_known_hash_good(certificate, known_hash)
hash = OpenSSL::Digest.new('SHA256').digest certificate
hash == known_hash
end

View File

@@ -0,0 +1,5 @@
require 'openssl'
def get_password_hash(password, salt)
OpenSSL::Digest.new('SHA256').digest(password + salt) # BAD
end

View File

@@ -0,0 +1,9 @@
require 'argon2'
def get_initial_hash(password)
Argon2::Password.create(password)
end
def check_password(password, known_hash)
Argon2::Password.verify_password(password, known_hash)
end

View File

@@ -0,0 +1,28 @@
edges
| weak_hashing.rb:3:1:3:8 | password | weak_hashing.rb:6:1:6:1 | x | provenance | |
| weak_hashing.rb:3:1:3:8 | password | weak_hashing.rb:10:23:10:30 | password | provenance | |
| weak_hashing.rb:3:1:3:8 | password | weak_hashing.rb:11:32:11:39 | password | provenance | |
| weak_hashing.rb:4:1:4:8 | username | weak_hashing.rb:12:23:12:30 | username | provenance | |
| weak_hashing.rb:6:1:6:1 | x | weak_hashing.rb:13:23:13:23 | x | provenance | |
| weak_hashing.rb:30:25:30:38 | password_param | weak_hashing.rb:32:25:32:38 | password_param | provenance | |
nodes
| weak_hashing.rb:3:1:3:8 | password | semmle.label | password |
| weak_hashing.rb:4:1:4:8 | username | semmle.label | username |
| weak_hashing.rb:6:1:6:1 | x | semmle.label | x |
| weak_hashing.rb:10:23:10:30 | password | semmle.label | password |
| weak_hashing.rb:11:32:11:39 | password | semmle.label | password |
| weak_hashing.rb:12:23:12:30 | username | semmle.label | username |
| weak_hashing.rb:13:23:13:23 | x | semmle.label | x |
| weak_hashing.rb:24:23:24:36 | call to get_password | semmle.label | call to get_password |
| weak_hashing.rb:28:23:28:42 | ...[...] | semmle.label | ...[...] |
| weak_hashing.rb:30:25:30:38 | password_param | semmle.label | password_param |
| weak_hashing.rb:32:25:32:38 | password_param | semmle.label | password_param |
subpaths
#select
| weak_hashing.rb:10:23:10:30 | password | weak_hashing.rb:3:1:3:8 | password | weak_hashing.rb:10:23:10:30 | password | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | weak_hashing.rb:3:1:3:8 | password | Sensitive data (password) |
| weak_hashing.rb:11:32:11:39 | password | weak_hashing.rb:3:1:3:8 | password | weak_hashing.rb:11:32:11:39 | password | $@ is used in a hashing algorithm (SHA1) that is insecure for password hashing, since it is not a computationally expensive hash function. | weak_hashing.rb:3:1:3:8 | password | Sensitive data (password) |
| weak_hashing.rb:12:23:12:30 | username | weak_hashing.rb:4:1:4:8 | username | weak_hashing.rb:12:23:12:30 | username | $@ is used in a hashing algorithm (MD5) that is insecure. | weak_hashing.rb:4:1:4:8 | username | Sensitive data (id) |
| weak_hashing.rb:13:23:13:23 | x | weak_hashing.rb:3:1:3:8 | password | weak_hashing.rb:13:23:13:23 | x | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | weak_hashing.rb:3:1:3:8 | password | Sensitive data (password) |
| weak_hashing.rb:24:23:24:36 | call to get_password | weak_hashing.rb:24:23:24:36 | call to get_password | weak_hashing.rb:24:23:24:36 | call to get_password | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | weak_hashing.rb:24:23:24:36 | call to get_password | Sensitive data (password) |
| weak_hashing.rb:28:23:28:42 | ...[...] | weak_hashing.rb:28:23:28:42 | ...[...] | weak_hashing.rb:28:23:28:42 | ...[...] | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | weak_hashing.rb:28:23:28:42 | ...[...] | Sensitive data (password) |
| weak_hashing.rb:32:25:32:38 | password_param | weak_hashing.rb:30:25:30:38 | password_param | weak_hashing.rb:32:25:32:38 | password_param | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | weak_hashing.rb:30:25:30:38 | password_param | Sensitive data (password) |

View File

@@ -0,0 +1 @@
queries/security/cwe-327/WeakSensitiveDataHashing.ql

View File

@@ -0,0 +1,33 @@
require 'openssl'
password = "abcde"
username = "some_user"
some_data = "foo"
x = password
Digest::MD5.hexdigest(some_data) # OK: input is not sensitive
Digest::SHA256.hexdigest(password) # OK: strong hash algorithm
Digest::MD5.hexdigest(password) # BAD: weak hash function used for sensitive data
OpenSSL::Digest.digest('SHA1', password) # BAD: weak hash function used for sensitive data
Digest::MD5.hexdigest(username) # BAD: weak hash function used for sensitive data
Digest::MD5.hexdigest(x) # BAD: weak hash function used for sensitive data
def get_safe_data()
return "hello"
end
def get_password()
return "changeme"
end
Digest::MD5.hexdigest(get_safe_data()) # OK: input is not sensitive
Digest::MD5.hexdigest(get_password()) # BAD: weak hash function used for sensitive data
some_hash = {password: "changeme", foo: "bar"}
Digest::MD5.hexdigest(some_hash[:foo]) # OK: input is not sensitive
Digest::MD5.hexdigest(some_hash[:password]) # BAD: weak hash function used for sensitive data
def a_method(safe_data, password_param)
Digest::MD5.hexdigest(safe_data) # OK: input is not sensitive
Digest::MD5.hexdigest(password_param) # BAD: weak hash function used for sensitive data
end

View File

@@ -124,8 +124,6 @@ signature module InputSig<LocationSig Location> {
string toString();
}
string ppReprType(DataFlowType t);
/**
* Holds if `t1` and `t2` are compatible types.
*

View File

@@ -3445,9 +3445,11 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
AccessPathApproxConsNil() { this = TConsNil(c, t) }
private string ppTyp() { result = t.toString() and result != "" }
override string toString() {
// The `concat` becomes "" if `ppReprType` has no result.
result = "[" + c.toString() + "]" + concat(" : " + ppReprType(t))
// The `concat` becomes "" if `ppTyp` has no result.
result = "[" + c.toString() + "]" + concat(" : " + this.ppTyp())
}
override Content getHead() { result = c }
@@ -3668,7 +3670,9 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
ParamNodeEx getParamNode() { result = p }
override string toString() { result = p + concat(" : " + ppReprType(t)) + " " + ap }
private string ppTyp() { result = t.toString() and result != "" }
override string toString() { result = p + concat(" : " + this.ppTyp()) + " " + ap }
Location getLocation() { result = p.getLocation() }
}
@@ -3935,10 +3939,12 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
override int length() { result = 1 + tail_.length() }
private string ppTyp() { result = t.toString() and result != "" }
private string toStringImpl(boolean needsSuffix) {
tail_ = TAccessPathNil() and
needsSuffix = false and
result = head_.toString() + "]" + concat(" : " + ppReprType(t))
result = head_.toString() + "]" + concat(" : " + this.ppTyp())
or
result = head_ + ", " + tail_.(AccessPathCons).toStringImpl(needsSuffix)
or
@@ -4087,9 +4093,8 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
private string ppType() {
this instanceof PathNodeSink and result = ""
or
exists(DataFlowType t | t = this.(PathNodeMid).getType() |
// The `concat` becomes "" if `ppReprType` has no result.
result = concat(" : " + ppReprType(t))
exists(string t | t = this.(PathNodeMid).getType().toString() |
if t = "" then result = "" else result = " : " + t
)
}
@@ -5402,9 +5407,8 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
private string ppType() {
this instanceof PartialPathNodeRev and result = ""
or
exists(DataFlowType t | t = this.(PartialPathNodeFwd).getType() |
// The `concat` becomes "" if `ppReprType` has no result.
result = concat(" : " + ppReprType(t))
exists(string t | t = this.(PartialPathNodeFwd).getType().toString() |
if t = "" then result = "" else result = " : " + t
)
}

View File

@@ -9,6 +9,8 @@ module;
signature predicate interpretModelForTestSig(QlBuiltins::ExtensionId madId, string model);
signature predicate queryResultsSig(string relation, int row, int column, string data);
signature class PathNodeSig {
string toString();
}
@@ -28,14 +30,14 @@ signature module PathGraphSig<PathNodeSig PathNode> {
predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out);
}
/** Transforms a `PathGraph` by printing the provenance information. */
module ShowProvenance<
interpretModelForTestSig/2 interpretModelForTest, PathNodeSig PathNode,
PathGraphSig<PathNode> PathGraph>
private signature predicate provenanceSig(string model);
private module TranslateModels<
interpretModelForTestSig/2 interpretModelForTest, provenanceSig/1 provenance>
{
private predicate madIds(string madId) {
exists(string model |
PathGraph::edges(_, _, _, model) and
provenance(model) and
model.regexpFind("(?<=MaD:)[0-9]*", _, _) = madId
)
}
@@ -44,14 +46,15 @@ module ShowProvenance<
madId = rank[r](string madId0 | madIds(madId0) | madId0 order by madId0.toInt())
}
query predicate models(int r, string model) {
/** Lists the renumbered and pretty-printed models used in the edges relation. */
predicate models(int r, string model) {
exists(QlBuiltins::ExtensionId madId |
rankedMadIds(madId.toString(), r) and interpretModelForTest(madId, model)
)
}
private predicate translateModelsPart(string model1, string model2, int i) {
PathGraph::edges(_, _, _, model1) and
provenance(model1) and
exists(string s | model1.splitAt("MaD:", i) = s |
model2 = s and i = 0
or
@@ -65,17 +68,29 @@ module ShowProvenance<
)
}
private predicate translateModels(string model1, string model2) {
predicate translateModels(string model1, string model2) {
exists(int i |
translateModelsPart(model1, model2, i) and
not translateModelsPart(model1, _, i + 1)
)
}
}
/** Transforms a `PathGraph` by printing the provenance information. */
module ShowProvenance<
interpretModelForTestSig/2 interpretModelForTest, PathNodeSig PathNode,
PathGraphSig<PathNode> PathGraph>
{
private predicate provenance(string model) { PathGraph::edges(_, _, _, model) }
private module Models = TranslateModels<interpretModelForTest/2, provenance/1>;
query predicate models(int r, string model) { Models::models(r, model) }
query predicate edges(PathNode a, PathNode b, string key, string val) {
exists(string model |
PathGraph::edges(a, b, key, model) and
translateModels(model, val)
Models::translateModels(model, val)
)
}
@@ -83,3 +98,36 @@ module ShowProvenance<
query predicate subpaths = PathGraph::subpaths/4;
}
/** Transforms a `PathGraph` by printing the provenance information. */
module TranslateProvenanceResults<
interpretModelForTestSig/2 interpretModelForTest, queryResultsSig/4 queryResults>
{
private int provenanceColumn() { result = 5 }
private predicate provenance(string model) { queryResults("edges", _, provenanceColumn(), model) }
private module Models = TranslateModels<interpretModelForTest/2, provenance/1>;
predicate results(string relation, int row, int column, string data) {
queryResults(relation, row, column, data) and
(relation != "edges" or column != provenanceColumn())
or
exists(string model |
relation = "edges" and
column = provenanceColumn() and
queryResults(relation, row, column, model) and
Models::translateModels(model, data)
)
or
exists(int r, string model |
Models::models(r, model) and
relation = "models" and
row = r
|
column = 0 and data = r.toString()
or
column = 1 and data = model
)
}
}

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* An error in the model for `URL.withUnsafeFileSystemRepresentation(_:)` has been corrected. This may result in new data flow paths being found during analysis.

View File

@@ -1310,9 +1310,6 @@ DataFlowType getNodeType(Node n) {
any() // return the singleton DataFlowType until we support type pruning for Swift
}
/** Gets a string representation of a `DataFlowType`. */
string ppReprType(DataFlowType t) { none() }
/**
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
* a node of type `t1` to a node of type `t2`.

View File

@@ -105,6 +105,7 @@ class ParameterNode extends Node instanceof ParameterNodeImpl {
}
/**
* A node in the data flow graph which corresponds to an SSA variable definition.
*/
class SsaDefinitionNode extends Node, TSsaDefinitionNode {
Ssa::Definition def;

View File

@@ -119,7 +119,7 @@ private class UrlSummaries extends SummaryModelCsv {
";URL;true;resourceValues(forKeys:);;;Argument[-1];ReturnValue;taint",
";URL;true;setResourceValues(_:);;;Argument[0];Argument[-1];taint",
";URL;true;setTemporaryResourceValue(_:forKey:);;;Argument[-1..0];Argument[-1];taint",
";URL;true;withUnsafeFileSystemRepresentation(_:);;;Argument[-1],Argument[0].Parameter[0];ReturnValue;taint",
";URL;true;withUnsafeFileSystemRepresentation(_:);;;Argument[-1];Argument[0].Parameter[0].OptionalSome.CollectionElement;taint",
";URL;true;withUnsafeFileSystemRepresentation(_:);;;Argument[0].ReturnValue;ReturnValue;taint",
";URL;true;resolvingSymlinksInPath();;;Argument[-1];ReturnValue;taint",
";URL;true;appendPathComponent(_:);;;Argument[-1..0];Argument[-1];taint",

View File

@@ -324,7 +324,7 @@ func taintThroughURL() {
})
urlTainted.withUnsafeFileSystemRepresentation({
ptr in
sink(any: ptr!) // $ MISSING: tainted=210
sink(any: ptr!) // $ tainted=210
})
sink(arg: urlTainted.resolvingSymlinksInPath()) // $ tainted=210