Merge remote-tracking branch 'origin/main' into nickrolfe/constant_regexp

This commit is contained in:
Nick Rolfe
2022-03-28 13:05:34 +01:00
108 changed files with 3671 additions and 1818 deletions

View File

@@ -16,9 +16,10 @@ jobs:
- uses: actions/checkout@v2
- name: Find codeql
id: find-codeql
uses: github/codeql-action/init@erik-krogh/ql
uses: github/codeql-action/init@aa93aea877e5fb8841bcb1193f672abf6e9f2980
with:
languages: javascript # does not matter
tools: latest
- name: Get CodeQL version
id: get-codeql-version
run: |
@@ -159,7 +160,7 @@ jobs:
PACK: ${{ runner.temp }}/pack
- name: Hack codeql-action options
run: |
JSON=$(jq -nc --arg pack "${PACK}" '.resolve.queries=["--search-path", $pack] | .resolve.extractor=["--search-path", $pack] | .database.init=["--search-path", $pack]')
JSON=$(jq -nc --arg pack "${PACK}" '.database."run-queries"=["--search-path", $pack] | .resolve.queries=["--search-path", $pack] | .resolve.extractor=["--search-path", $pack] | .database.init=["--search-path", $pack]')
echo "CODEQL_ACTION_EXTRA_OPTIONS=${JSON}" >> ${GITHUB_ENV}
env:
PACK: ${{ runner.temp }}/pack
@@ -171,22 +172,25 @@ jobs:
echo "paths:" > ${CONF}
echo " - ${FOLDER}" >> ${CONF}
echo "paths-ignore:" >> ${CONF}
echo " - ql/ql/test" >> ${CONF}
echo " - ql/ql/test" >> ${CONF}
echo "disable-default-queries: true" >> ${CONF}
echo "packs:" >> ${CONF}
echo " - codeql/ql" >> ${CONF}
echo "Config file: "
cat ${CONF}
env:
CONF: ./ql-for-ql-config.yml
FOLDER: ${{ matrix.folder }}
- name: Initialize CodeQL
uses: github/codeql-action/init@erik-krogh/ql
uses: github/codeql-action/init@aa93aea877e5fb8841bcb1193f672abf6e9f2980
with:
languages: ql
db-location: ${{ runner.temp }}/db
config-file: ./ql-for-ql-config.yml
tools: latest
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@erik-krogh/ql
uses: github/codeql-action/analyze@aa93aea877e5fb8841bcb1193f672abf6e9f2980
with:
category: "ql-for-ql-${{ matrix.folder }}"
- name: Copy sarif file to CWD

View File

@@ -26,7 +26,7 @@ jobs:
- name: Find codeql
id: find-codeql
uses: github/codeql-action/init@erik-krogh/ql
uses: github/codeql-action/init@aa93aea877e5fb8841bcb1193f672abf6e9f2980
with:
languages: javascript # does not matter
- uses: actions/cache@v2

View File

@@ -20,7 +20,7 @@ jobs:
- uses: actions/checkout@v2
- name: Find codeql
id: find-codeql
uses: github/codeql-action/init@erik-krogh/ql
uses: github/codeql-action/init@aa93aea877e5fb8841bcb1193f672abf6e9f2980
with:
languages: javascript # does not matter
- uses: actions/cache@v2

View File

@@ -161,8 +161,13 @@ class IRBlock extends IRBlockBase {
*/
pragma[noinline]
final IRBlock dominanceFrontier() {
this.dominates(result.getAPredecessor()) and
not this.strictlyDominates(result)
this.getASuccessor() = result and
not this.immediatelyDominates(result)
or
exists(IRBlock prev | result = prev.dominanceFrontier() |
this.immediatelyDominates(prev) and
not this.immediatelyDominates(result)
)
}
/**
@@ -201,8 +206,13 @@ class IRBlock extends IRBlockBase {
*/
pragma[noinline]
final IRBlock postDominanceFrontier() {
this.postDominates(result.getASuccessor()) and
not this.strictlyPostDominates(result)
this.getAPredecessor() = result and
not this.immediatelyPostDominates(result)
or
exists(IRBlock prev | result = prev.postDominanceFrontier() |
this.immediatelyPostDominates(prev) and
not this.immediatelyPostDominates(result)
)
}
/**

View File

@@ -161,8 +161,13 @@ class IRBlock extends IRBlockBase {
*/
pragma[noinline]
final IRBlock dominanceFrontier() {
this.dominates(result.getAPredecessor()) and
not this.strictlyDominates(result)
this.getASuccessor() = result and
not this.immediatelyDominates(result)
or
exists(IRBlock prev | result = prev.dominanceFrontier() |
this.immediatelyDominates(prev) and
not this.immediatelyDominates(result)
)
}
/**
@@ -201,8 +206,13 @@ class IRBlock extends IRBlockBase {
*/
pragma[noinline]
final IRBlock postDominanceFrontier() {
this.postDominates(result.getASuccessor()) and
not this.strictlyPostDominates(result)
this.getAPredecessor() = result and
not this.immediatelyPostDominates(result)
or
exists(IRBlock prev | result = prev.postDominanceFrontier() |
this.immediatelyPostDominates(prev) and
not this.immediatelyPostDominates(result)
)
}
/**

View File

@@ -154,13 +154,6 @@ private predicate ignoreSideEffects(Expr expr) {
* around extractor bugs. Once the relevant extractor bugs are fixed, this predicate can be removed.
*/
private predicate isInvalidFunction(Function func) {
exists(Literal literal |
// Constructor field inits within a compiler-generated copy constructor have a source expression
// that is a `Literal` with no value.
literal = func.(Constructor).getAnInitializer().(ConstructorFieldInit).getExpr() and
not exists(literal.getValue())
)
or
exists(ThisExpr thisExpr |
// An instantiation of a member function template is not treated as a `MemberFunction` if it has
// only non-type template arguments.

View File

@@ -161,8 +161,13 @@ class IRBlock extends IRBlockBase {
*/
pragma[noinline]
final IRBlock dominanceFrontier() {
this.dominates(result.getAPredecessor()) and
not this.strictlyDominates(result)
this.getASuccessor() = result and
not this.immediatelyDominates(result)
or
exists(IRBlock prev | result = prev.dominanceFrontier() |
this.immediatelyDominates(prev) and
not this.immediatelyDominates(result)
)
}
/**
@@ -201,8 +206,13 @@ class IRBlock extends IRBlockBase {
*/
pragma[noinline]
final IRBlock postDominanceFrontier() {
this.postDominates(result.getASuccessor()) and
not this.strictlyPostDominates(result)
this.getAPredecessor() = result and
not this.immediatelyPostDominates(result)
or
exists(IRBlock prev | result = prev.postDominanceFrontier() |
this.immediatelyPostDominates(prev) and
not this.immediatelyPostDominates(result)
)
}
/**

View File

@@ -3,17 +3,17 @@
"qhelp.dtd">
<qhelp>
<overview>
<p>Exposing system data or debugging information may help an adversary to learn about the system and form an attack plan. An attacker can use error messages that reveal technologies, operating systems, and product versions to tune their attack against known vulnerabilities in these technologies.</p>
<p>Exposing system data or debugging information may help a malicious user learn about the system and form an attack plan. An attacker can use error messages that reveal technologies, operating systems, and product versions to tune their attack against known vulnerabilities in the software.</p>
<p>This query finds locations where system configuration information might be revealed to a user.</p>
<p>This query finds locations where system configuration information might be revealed to a remote user.</p>
</overview>
<recommendation>
<p>Do not expose system configuration information to users. Be wary of the difference between information that could be helpful to users, and unnecessary details that could be useful to an adversary.</p>
<p>Do not expose system configuration information to remote users. Be wary of the difference between information that could be helpful to users, and unnecessary details that could be useful to a malicious user.</p>
</recommendation>
<example>
<p>In this example the value of the <code>PATH</code> environment variable is revealed in full to the user when a particular error occurs. This might reveal information such as the software installed on your system to an adversary who does not have legitimate access to that information.</p>
<p>In this example the value of the <code>PATH</code> environment variable is revealed in full to the user when a particular error occurs. This might reveal information such as the software installed on your system to a malicious user who does not have legitimate access to that information.</p>
<sample src="ExposedSystemDataIncorrect.cpp" />

View File

@@ -1,7 +1,7 @@
/**
* @name Exposure of system data to an unauthorized control sphere
* @description Exposing system data or debugging information helps
* an adversary learn about the system and form an
* a malicious user learn about the system and form an
* attack plan.
* @kind path-problem
* @problem.severity warning
@@ -13,284 +13,15 @@
*/
import cpp
import semmle.code.cpp.commons.Environment
import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.models.interfaces.FlowSource
import DataFlow::PathGraph
/**
* An element that should not be exposed to an adversary.
*/
abstract class SystemData extends Element {
/**
* Gets an expression that is part of this `SystemData`.
*/
abstract Expr getAnExpr();
}
/**
* Data originating from the environment.
*/
class EnvData extends SystemData {
EnvData() {
// identify risky looking environment variables only
this.(EnvironmentRead)
.getEnvironmentVariable()
.toLowerCase()
.regexpMatch(".*(user|host|admin|root|home|path|http|ssl|snmp|sock|port|proxy|pass|token|crypt|key).*")
}
override Expr getAnExpr() { result = this }
}
/**
* Data originating from a call to `mysql_get_client_info()`.
*/
class SqlClientInfo extends SystemData {
SqlClientInfo() { this.(FunctionCall).getTarget().hasName("mysql_get_client_info") }
override Expr getAnExpr() { result = this }
}
private predicate sqlConnectInfo(FunctionCall source, VariableAccess use) {
(
source.getTarget().hasName("mysql_connect") or
source.getTarget().hasName("mysql_real_connect")
) and
use = source.getAnArgument()
}
/**
* Data passed into an SQL connect function.
*/
class SqlConnectInfo extends SystemData {
SqlConnectInfo() { sqlConnectInfo(this, _) }
override Expr getAnExpr() { sqlConnectInfo(this, result) }
}
private predicate posixSystemInfo(FunctionCall source, Element use) {
// size_t confstr(int name, char *buf, size_t len)
// - various OS / system strings, such as the libc version
// int statvfs(const char *__path, struct statvfs *__buf)
// int fstatvfs(int __fd, struct statvfs *__buf)
// - various filesystem parameters
// int uname(struct utsname *buf)
// - OS name and version
source.getTarget().hasName(["confstr", "statvfs", "fstatvfs", "uname"]) and
use = source.getArgument(1)
}
/**
* Data obtained from a POSIX system information call.
*/
class PosixSystemInfo extends SystemData {
PosixSystemInfo() { posixSystemInfo(this, _) }
override Expr getAnExpr() { posixSystemInfo(this, result) }
}
private predicate posixPWInfo(FunctionCall source, Element use) {
// struct passwd *getpwnam(const char *name);
// struct passwd *getpwuid(uid_t uid);
// struct passwd *getpwent(void);
// struct group *getgrnam(const char *name);
// struct group *getgrgid(gid_t);
// struct group *getgrent(void);
source
.getTarget()
.hasName(["getpwnam", "getpwuid", "getpwent", "getgrnam", "getgrgid", "getgrent"]) and
use = source
or
// int getpwnam_r(const char *name, struct passwd *pwd,
// char *buf, size_t buflen, struct passwd **result);
// int getpwuid_r(uid_t uid, struct passwd *pwd,
// char *buf, size_t buflen, struct passwd **result);
// int getgrgid_r(gid_t gid, struct group *grp,
// char *buf, size_t buflen, struct group **result);
// int getgrnam_r(const char *name, struct group *grp,
// char *buf, size_t buflen, struct group **result);
source.getTarget().hasName(["getpwnam_r", "getpwuid_r", "getgrgid_r", "getgrnam_r"]) and
use = source.getArgument([1, 2, 4])
or
// int getpwent_r(struct passwd *pwd, char *buffer, size_t bufsize,
// struct passwd **result);
// int getgrent_r(struct group *gbuf, char *buf,
// size_t buflen, struct group **gbufp);
source.getTarget().hasName(["getpwent_r", "getgrent_r"]) and
use = source.getArgument([0, 1, 3])
}
/**
* Data obtained from a POSIX user/password/group database information call.
*/
class PosixPWInfo extends SystemData {
PosixPWInfo() { posixPWInfo(this, _) }
override Expr getAnExpr() { posixPWInfo(this, result) }
}
private predicate windowsSystemInfo(FunctionCall source, Element use) {
// DWORD WINAPI GetVersion(void);
source.getTarget().hasGlobalName("GetVersion") and
use = source
or
// BOOL WINAPI GetVersionEx(_Inout_ LPOSVERSIONINFO lpVersionInfo);
// void WINAPI GetSystemInfo(_Out_ LPSYSTEM_INFO lpSystemInfo);
// void WINAPI GetNativeSystemInfo(_Out_ LPSYSTEM_INFO lpSystemInfo);
source
.getTarget()
.hasGlobalName([
"GetVersionEx", "GetVersionExA", "GetVersionExW", "GetSystemInfo", "GetNativeSystemInfo"
]) and
use = source.getArgument(0)
}
/**
* Data obtained from a Windows system information call.
*/
class WindowsSystemInfo extends SystemData {
WindowsSystemInfo() { windowsSystemInfo(this, _) }
override Expr getAnExpr() { windowsSystemInfo(this, result) }
}
private predicate windowsFolderPath(FunctionCall source, Element use) {
// BOOL SHGetSpecialFolderPath(
// HWND hwndOwner,
// _Out_ LPTSTR lpszPath,
// _In_ int csidl,
// _In_ BOOL fCreate
// );
source
.getTarget()
.hasGlobalName([
"SHGetSpecialFolderPath", "SHGetSpecialFolderPathA", "SHGetSpecialFolderPathW"
]) and
use = source.getArgument(1)
or
// HRESULT SHGetKnownFolderPath(
// _In_ REFKNOWNFOLDERID rfid,
// _In_ DWORD dwFlags,
// _In_opt_ HANDLE hToken,
// _Out_ PWSTR *ppszPath
// );
source.getTarget().hasGlobalName("SHGetKnownFolderPath") and
use = source.getArgument(3)
or
// HRESULT SHGetFolderPath(
// _In_ HWND hwndOwner,
// _In_ int nFolder,
// _In_ HANDLE hToken,
// _In_ DWORD dwFlags,
// _Out_ LPTSTR pszPath
// );
source.getTarget().hasGlobalName(["SHGetFolderPath", "SHGetFolderPathA", "SHGetFolderPathW"]) and
use = source.getArgument(4)
or
// HRESULT SHGetFolderPathAndSubDir(
// _In_ HWND hwnd,
// _In_ int csidl,
// _In_ HANDLE hToken,
// _In_ DWORD dwFlags,
// _In_ LPCTSTR pszSubDir,
// _Out_ LPTSTR pszPath
// );
source
.getTarget()
.hasGlobalName([
"SHGetFolderPathAndSubDir", "SHGetFolderPathAndSubDirA", "SHGetFolderPathAndSubDirW"
]) and
use = source.getArgument(5)
}
/**
* Data obtained about Windows special paths (for example, the
* location of `System32`).
*/
class WindowsFolderPath extends SystemData {
WindowsFolderPath() { windowsFolderPath(this, _) }
override Expr getAnExpr() { windowsFolderPath(this, result) }
}
private predicate logonUser(FunctionCall source, VariableAccess use) {
source.getTarget().hasGlobalName(["LogonUser", "LogonUserW", "LogonUserA"]) and
use = source.getAnArgument()
}
/**
* Data passed into a `LogonUser` (Windows) function.
*/
class LogonUser extends SystemData {
LogonUser() { logonUser(this, _) }
override Expr getAnExpr() { logonUser(this, result) }
}
private predicate regQuery(FunctionCall source, VariableAccess use) {
// LONG WINAPI RegQueryValue(
// _In_ HKEY hKey,
// _In_opt_ LPCTSTR lpSubKey,
// _Out_opt_ LPTSTR lpValue,
// _Inout_opt_ PLONG lpcbValue
// );
source.getTarget().hasGlobalName(["RegQueryValue", "RegQueryValueA", "RegQueryValueW"]) and
use = source.getArgument(2)
or
// LONG WINAPI RegQueryMultipleValues(
// _In_ HKEY hKey,
// _Out_ PVALENT val_list,
// _In_ DWORD num_vals,
// _Out_opt_ LPTSTR lpValueBuf,
// _Inout_opt_ LPDWORD ldwTotsize
// );
source
.getTarget()
.hasGlobalName([
"RegQueryMultipleValues", "RegQueryMultipleValuesA", "RegQueryMultipleValuesW"
]) and
use = source.getArgument(3)
or
// LONG WINAPI RegQueryValueEx(
// _In_ HKEY hKey,
// _In_opt_ LPCTSTR lpValueName,
// _Reserved_ LPDWORD lpReserved,
// _Out_opt_ LPDWORD lpType,
// _Out_opt_ LPBYTE lpData,
// _Inout_opt_ LPDWORD lpcbData
// );
source.getTarget().hasGlobalName(["RegQueryValueEx", "RegQueryValueExA", "RegQueryValueExW"]) and
use = source.getArgument(4)
or
// LONG WINAPI RegGetValue(
// _In_ HKEY hkey,
// _In_opt_ LPCTSTR lpSubKey,
// _In_opt_ LPCTSTR lpValue,
// _In_opt_ DWORD dwFlags,
// _Out_opt_ LPDWORD pdwType,
// _Out_opt_ PVOID pvData,
// _Inout_opt_ LPDWORD pcbData
// );
source.getTarget().hasGlobalName(["RegGetValue", "RegGetValueA", "RegGetValueW"]) and
use = source.getArgument(5)
}
/**
* Data read from the Windows registry.
*/
class RegQuery extends SystemData {
RegQuery() { regQuery(this, _) }
override Expr getAnExpr() { regQuery(this, result) }
}
import SystemData
class ExposedSystemDataConfiguration extends TaintTracking::Configuration {
ExposedSystemDataConfiguration() { this = "ExposedSystemDataConfiguration" }
override predicate isSource(DataFlow::Node source) {
source.asConvertedExpr() = any(SystemData sd).getAnExpr()
}
override predicate isSource(DataFlow::Node source) { source = any(SystemData sd).getAnExpr() }
override predicate isSink(DataFlow::Node sink) {
exists(FunctionCall fc, FunctionInput input, int arg |

View File

@@ -2,6 +2,5 @@ char* path = getenv("PATH");
//...
fprintf(stderr, "A required executable file could not be found. " \
"Please ensure that the software has been installed " \
"correctly or contact a system administrator.\n");
message = "An internal error has occurred. Please try again or contact a system administrator.\n";
send(socket, message, strlen(message), 0);

View File

@@ -2,4 +2,5 @@ char* path = getenv("PATH");
//...
fprintf(stderr, "cannot find exe on path %s\n", path);
sprintf(buffer, "Cannot find exe on path: %s", path);
send(socket, buffer, strlen(buffer), 0);

View File

@@ -0,0 +1,28 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Exposing system data or debugging information may help a malicious user learn about the system and form an attack plan. An attacker can use error messages that reveal technologies, operating systems, and product versions to tune their attack against known vulnerabilities in the software.</p>
<p>This query finds locations where system configuration information that is particularly sensitive might be revealed to a user.</p>
</overview>
<recommendation>
<p>Do not expose system configuration information to users. Be wary of the difference between information that could be helpful to users, and unnecessary details that could be useful to a malicious user.</p>
</recommendation>
<example>
<p>In this example the value of the <code>PATH</code> environment variable is revealed in full to the user when a particular error occurs. This might reveal information such as the software installed on your system to a malicious user who does not have legitimate access to that information.</p>
<sample src="PotentiallyExposedSystemDataIncorrect.cpp" />
<p>The message should be rephrased without this information, for example:</p>
<sample src="PotentiallyExposedSystemDataCorrect.cpp" />
</example>
<references>
</references>
</qhelp>

View File

@@ -0,0 +1,51 @@
/**
* @name Potential exposure of sensitive system data to an unauthorized control sphere
* @description Exposing sensitive system data helps
* a malicious user learn about the system and form an
* attack plan.
* @kind path-problem
* @problem.severity warning
* @security-severity 6.5
* @precision medium
* @id cpp/potential-system-data-exposure
* @tags security
* external/cwe/cwe-497
*/
/*
* These queries are closely related:
* - `cpp/system-data-exposure`, which flags exposure of system information
* to a remote sink (i.e. focusses on quality of the sink).
* - `cpp/potential-system-data-exposure`, which flags on exposure of the most
* sensitive information to a local sink (i.e. focusses on quality of the
* sensitive information).
*
* This used to be a single query with neither focus, which was too noisy and
* gave the user less control.
*/
import cpp
import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.models.interfaces.FlowSource
import semmle.code.cpp.security.OutputWrite
import DataFlow::PathGraph
import SystemData
class PotentiallyExposedSystemDataConfiguration extends TaintTracking::Configuration {
PotentiallyExposedSystemDataConfiguration() { this = "PotentiallyExposedSystemDataConfiguration" }
override predicate isSource(DataFlow::Node source) {
source = any(SystemData sd | sd.isSensitive()).getAnExpr()
}
override predicate isSink(DataFlow::Node sink) {
exists(OutputWrite ow | ow.getASource().getAChild*() = sink.asExpr())
}
}
from
PotentiallyExposedSystemDataConfiguration config, DataFlow::PathNode source,
DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink, source, sink, "This operation potentially exposes sensitive system data from $@.",
source, source.getNode().toString()

View File

@@ -0,0 +1,5 @@
char* key = getenv("APP_KEY");
//...
fprintf(stderr, "Application key not recognized. Please ensure the key is correct or contact a system administrator.\n", key);

View File

@@ -0,0 +1,5 @@
char* key = getenv("APP_KEY");
//...
fprintf(stderr, "Key not recognized: %s\n", key);

View File

@@ -0,0 +1,342 @@
/**
* Classes for recognizing system data, used by the exposed system data queries.
*/
import cpp
import semmle.code.cpp.commons.Environment
import semmle.code.cpp.ir.dataflow.TaintTracking
/**
* An element that should not be exposed to a malicious user.
*/
abstract class SystemData extends Element {
/**
* Gets an expression that is part of this `SystemData`.
*/
abstract DataFlow::Node getAnExpr();
/**
* Holds if this system data is considered especially sensitive (for example
* a password or token).
*/
predicate isSensitive() { none() }
}
/**
* Data originating from the environment.
*/
class EnvData extends SystemData {
EnvData() {
// identify risky looking environment variables only
this.(EnvironmentRead)
.getEnvironmentVariable()
.toLowerCase()
.regexpMatch(".*(user|host|admin|root|home|path|http|ssl|snmp|sock|port|proxy|pass|token|crypt|key).*")
}
override DataFlow::Node getAnExpr() { result.asConvertedExpr() = this }
override predicate isSensitive() {
this.(EnvironmentRead)
.getEnvironmentVariable()
.toLowerCase()
.regexpMatch(".*(pass|token|key).*")
}
}
/**
* Data originating from a call to `mysql_get_client_info()`.
*/
class SQLClientInfo extends SystemData {
SQLClientInfo() { this.(FunctionCall).getTarget().hasName("mysql_get_client_info") }
override DataFlow::Node getAnExpr() { result.asConvertedExpr() = this }
override predicate isSensitive() { any() }
}
private predicate sqlConnectInfo(FunctionCall source, Expr use) {
(
source.getTarget().hasName("mysql_connect") or
source.getTarget().hasName("mysql_real_connect")
) and
use = source.getAnArgument()
}
/**
* Data passed into an SQL connect function.
*/
class SQLConnectInfo extends SystemData {
SQLConnectInfo() { sqlConnectInfo(this, _) }
override DataFlow::Node getAnExpr() { sqlConnectInfo(this, result.asConvertedExpr()) }
override predicate isSensitive() { any() }
}
private predicate posixSystemInfo(FunctionCall source, DataFlow::Node use) {
// size_t confstr(int name, char *buf, size_t len)
// - various OS / system strings, such as the libc version
// int statvfs(const char *__path, struct statvfs *__buf)
// int fstatvfs(int __fd, struct statvfs *__buf)
source.getTarget().hasName(["confstr", "statvfs", "fstatvfs"]) and
use.asDefiningArgument() = source.getArgument(1)
or
// - various filesystem parameters
// int uname(struct utsname *buf)
// - OS name and version
source.getTarget().hasName("uname") and
use.asDefiningArgument() = source.getArgument(0)
}
/**
* Data obtained from a POSIX system information call.
*/
class PosixSystemInfo extends SystemData {
PosixSystemInfo() { posixSystemInfo(this, _) }
override DataFlow::Node getAnExpr() { posixSystemInfo(this, result) }
}
private predicate posixPWInfo(FunctionCall source, DataFlow::Node use) {
// struct passwd *getpwnam(const char *name);
// struct passwd *getpwuid(uid_t uid);
// struct passwd *getpwent(void);
// struct group *getgrnam(const char *name);
// struct group *getgrgid(gid_t);
// struct group *getgrent(void);
source
.getTarget()
.hasName(["getpwnam", "getpwuid", "getpwent", "getgrnam", "getgrgid", "getgrent"]) and
use.asConvertedExpr() = source
or
// int getpwnam_r(const char *name, struct passwd *pwd,
// char *buf, size_t buflen, struct passwd **result);
// int getpwuid_r(uid_t uid, struct passwd *pwd,
// char *buf, size_t buflen, struct passwd **result);
// int getgrgid_r(gid_t gid, struct group *grp,
// char *buf, size_t buflen, struct group **result);
// int getgrnam_r(const char *name, struct group *grp,
// char *buf, size_t buflen, struct group **result);
source.getTarget().hasName(["getpwnam_r", "getpwuid_r", "getgrgid_r", "getgrnam_r"]) and
(
use.asConvertedExpr() = source.getArgument([1, 2]) or
use.asDefiningArgument() = source.getArgument(4)
)
or
// int getpwent_r(struct passwd *pwd, char *buffer, size_t bufsize,
// struct passwd **result);
// int getgrent_r(struct group *gbuf, char *buf,
// size_t buflen, struct group **gbufp);
source.getTarget().hasName(["getpwent_r", "getgrent_r"]) and
(
use.asConvertedExpr() = source.getArgument([0, 1]) or
use.asDefiningArgument() = source.getArgument(3)
)
}
/**
* Data obtained from a POSIX user/password/group database information call.
*/
class PosixPWInfo extends SystemData {
PosixPWInfo() { posixPWInfo(this, _) }
override DataFlow::Node getAnExpr() { posixPWInfo(this, result) }
override predicate isSensitive() { any() }
}
private predicate windowsSystemInfo(FunctionCall source, DataFlow::Node use) {
// DWORD WINAPI GetVersion(void);
source.getTarget().hasGlobalName("GetVersion") and
use.asConvertedExpr() = source
or
// BOOL WINAPI GetVersionEx(_Inout_ LPOSVERSIONINFO lpVersionInfo);
// void WINAPI GetSystemInfo(_Out_ LPSYSTEM_INFO lpSystemInfo);
// void WINAPI GetNativeSystemInfo(_Out_ LPSYSTEM_INFO lpSystemInfo);
source
.getTarget()
.hasGlobalName([
"GetVersionEx", "GetVersionExA", "GetVersionExW", "GetSystemInfo", "GetNativeSystemInfo"
]) and
use.asDefiningArgument() = source.getArgument(0)
}
/**
* Data obtained from a Windows system information call.
*/
class WindowsSystemInfo extends SystemData {
WindowsSystemInfo() { windowsSystemInfo(this, _) }
override DataFlow::Node getAnExpr() { windowsSystemInfo(this, result) }
}
private predicate windowsFolderPath(FunctionCall source, Element use) {
// BOOL SHGetSpecialFolderPath(
// HWND hwndOwner,
// _Out_ LPTSTR lpszPath,
// _In_ int csidl,
// _In_ BOOL fCreate
// );
source
.getTarget()
.hasGlobalName([
"SHGetSpecialFolderPath", "SHGetSpecialFolderPathA", "SHGetSpecialFolderPathW"
]) and
use = source.getArgument(1)
or
// HRESULT SHGetKnownFolderPath(
// _In_ REFKNOWNFOLDERID rfid,
// _In_ DWORD dwFlags,
// _In_opt_ HANDLE hToken,
// _Out_ PWSTR *ppszPath
// );
source.getTarget().hasGlobalName("SHGetKnownFolderPath") and
use = source.getArgument(3)
or
// HRESULT SHGetFolderPath(
// _In_ HWND hwndOwner,
// _In_ int nFolder,
// _In_ HANDLE hToken,
// _In_ DWORD dwFlags,
// _Out_ LPTSTR pszPath
// );
source.getTarget().hasGlobalName(["SHGetFolderPath", "SHGetFolderPathA", "SHGetFolderPathW"]) and
use = source.getArgument(4)
or
// HRESULT SHGetFolderPathAndSubDir(
// _In_ HWND hwnd,
// _In_ int csidl,
// _In_ HANDLE hToken,
// _In_ DWORD dwFlags,
// _In_ LPCTSTR pszSubDir,
// _Out_ LPTSTR pszPath
// );
source
.getTarget()
.hasGlobalName([
"SHGetFolderPathAndSubDir", "SHGetFolderPathAndSubDirA", "SHGetFolderPathAndSubDirW"
]) and
use = source.getArgument(5)
}
/**
* Data obtained about Windows special paths (for example, the
* location of `System32`).
*/
class WindowsFolderPath extends SystemData {
WindowsFolderPath() { windowsFolderPath(this, _) }
override DataFlow::Node getAnExpr() { windowsFolderPath(this, result.asDefiningArgument()) }
}
private predicate logonUser(FunctionCall source, VariableAccess use) {
source.getTarget().hasGlobalName(["LogonUser", "LogonUserW", "LogonUserA"]) and
use = source.getAnArgument()
}
/**
* Data passed into a `LogonUser` (Windows) function.
*/
class LogonUser extends SystemData {
LogonUser() { logonUser(this, _) }
override DataFlow::Node getAnExpr() { logonUser(this, result.asConvertedExpr()) }
override predicate isSensitive() { any() }
}
/**
* The type of a registry query parameter, if it is of interest to us. This
* is used to express information about registry query parameters in the
* `regQuery` predicate concisely.
*/
private newtype TRegQueryParameter =
TSubKeyName() or
TValueName() or
TReturnData()
/**
* Registry query call (`source`) with information about parameters (`param`).
*/
private predicate regQuery(FunctionCall source, TRegQueryParameter paramType, Expr param) {
// LONG WINAPI RegQueryValue(
// _In_ HKEY hKey,
// _In_opt_ LPCTSTR lpSubKey,
// _Out_opt_ LPTSTR lpValue,
// _Inout_opt_ PLONG lpcbValue
// );
source.getTarget().hasGlobalName(["RegQueryValue", "RegQueryValueA", "RegQueryValueW"]) and
(
paramType = TSubKeyName() and param = source.getArgument(1)
or
paramType = TReturnData() and param = source.getArgument(2)
)
or
// LONG WINAPI RegQueryMultipleValues(
// _In_ HKEY hKey,
// _Out_ PVALENT val_list,
// _In_ DWORD num_vals,
// _Out_opt_ LPTSTR lpValueBuf,
// _Inout_opt_ LPDWORD ldwTotsize
// );
source
.getTarget()
.hasGlobalName([
"RegQueryMultipleValues", "RegQueryMultipleValuesA", "RegQueryMultipleValuesW"
]) and
paramType = TReturnData() and
param = source.getArgument(3)
or
// LONG WINAPI RegQueryValueEx(
// _In_ HKEY hKey,
// _In_opt_ LPCTSTR lpValueName,
// _Reserved_ LPDWORD lpReserved,
// _Out_opt_ LPDWORD lpType,
// _Out_opt_ LPBYTE lpData,
// _Inout_opt_ LPDWORD lpcbData
// );
source.getTarget().hasGlobalName(["RegQueryValueEx", "RegQueryValueExA", "RegQueryValueExW"]) and
(
paramType = TValueName() and param = source.getArgument(1)
or
paramType = TReturnData() and param = source.getArgument(4)
)
or
// LONG WINAPI RegGetValue(
// _In_ HKEY hkey,
// _In_opt_ LPCTSTR lpSubKey,
// _In_opt_ LPCTSTR lpValue,
// _In_opt_ DWORD dwFlags,
// _Out_opt_ LPDWORD pdwType,
// _Out_opt_ PVOID pvData,
// _Inout_opt_ LPDWORD pcbData
// );
source.getTarget().hasGlobalName(["RegGetValue", "RegGetValueA", "RegGetValueW"]) and
(
paramType = TSubKeyName() and param = source.getArgument(1)
or
paramType = TValueName() and param = source.getArgument(2)
or
paramType = TReturnData() and param = source.getArgument(5)
)
}
/**
* Data read from the Windows registry.
*/
class RegQuery extends SystemData {
RegQuery() { regQuery(this, _, _) }
override DataFlow::Node getAnExpr() { regQuery(this, TReturnData(), result.asDefiningArgument()) }
override predicate isSensitive() {
exists(Expr e |
(
regQuery(this, TSubKeyName(), e) or
regQuery(this, TValueName(), e)
) and
e.getValue().toLowerCase().regexpMatch(".*(pass|token|key).*")
)
}
}

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* A new query, "Potential exposure of sensitive system data to an unauthorized control sphere" (`cpp/potential-system-data-exposure`) has been added. This query is focused on exposure of information that is highly likely to be sensitive, whereas the similar query "Exposure of system data to an unauthorized control sphere" (`cpp/system-data-exposure`) is focused on exposure of information on a channel that is more likely to be intercepted by an attacker.

View File

@@ -3,4 +3,3 @@
- apply: code-scanning-selectors.yml
from: codeql/suite-helpers
- apply: codeql-suites/exclude-slow-queries.yml
from: codeql/cpp-queries

View File

@@ -3,7 +3,6 @@
- apply: lgtm-selectors.yml
from: codeql/suite-helpers
- apply: codeql-suites/exclude-slow-queries.yml
from: codeql/cpp-queries
# These are only for IDE use.
- exclude:
tags contain:

View File

@@ -3,4 +3,3 @@
- apply: security-and-quality-selectors.yml
from: codeql/suite-helpers
- apply: codeql-suites/exclude-slow-queries.yml
from: codeql/cpp-queries

View File

@@ -3,4 +3,3 @@
- apply: security-extended-selectors.yml
from: codeql/suite-helpers
- apply: codeql-suites/exclude-slow-queries.yml
from: codeql/cpp-queries

View File

@@ -208,11 +208,16 @@ postWithInFlow
| lambdas.cpp:13:7:13:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:13:10:17:2 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:13:10:17:2 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:13:11:13:11 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:13:11:13:11 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:16:3:16:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:20:7:20:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:20:10:24:2 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:20:10:24:2 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:20:10:24:2 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:20:11:20:11 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:20:11:20:11 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:20:11:20:11 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:23:3:23:3 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:23:3:23:14 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:23:3:23:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
@@ -220,6 +225,8 @@ postWithInFlow
| lambdas.cpp:28:7:28:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:28:10:31:2 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:28:10:31:2 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:28:11:28:11 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:28:11:28:11 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:34:7:34:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:34:13:34:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:40:7:40:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |

View File

@@ -27,7 +27,8 @@
| arrayassignment.cpp:29:8:29:13 | call to source | arrayassignment.cpp:29:2:29:15 | ... = ... | |
| arrayassignment.cpp:29:8:29:13 | call to source | arrayassignment.cpp:33:7:33:9 | r_x | |
| arrayassignment.cpp:32:8:32:10 | p_x | arrayassignment.cpp:32:7:32:10 | * ... | TAINT |
| arrayassignment.cpp:37:7:37:7 | Unknown literal | arrayassignment.cpp:37:7:37:7 | constructor init of field i | TAINT |
| arrayassignment.cpp:37:7:37:7 | i | arrayassignment.cpp:37:7:37:7 | constructor init of field i | TAINT |
| arrayassignment.cpp:37:7:37:7 | i | arrayassignment.cpp:37:7:37:7 | i | |
| arrayassignment.cpp:37:7:37:7 | this | arrayassignment.cpp:37:7:37:7 | constructor init of field i [pre-this] | |
| arrayassignment.cpp:40:2:40:6 | this | arrayassignment.cpp:40:12:40:15 | constructor init of field i [pre-this] | |
| arrayassignment.cpp:40:12:40:15 | 0 | arrayassignment.cpp:40:12:40:15 | constructor init of field i | TAINT |
@@ -284,6 +285,7 @@
| copyableclass_declonly.cpp:67:13:67:18 | call to source | copyableclass_declonly.cpp:67:13:67:20 | call to MyCopyableClassDeclOnly | TAINT |
| copyableclass_declonly.cpp:67:13:67:20 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:67:8:67:9 | ref arg s3 | TAINT |
| copyableclass_declonly.cpp:67:13:67:20 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:67:11:67:11 | call to operator= | TAINT |
| file://:0:0:0:0 | (unnamed parameter 0) | arrayassignment.cpp:37:7:37:7 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | file://:0:0:0:0 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | file://:0:0:0:0 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | file://:0:0:0:0 | (unnamed parameter 0) | |
@@ -299,6 +301,27 @@
| file://:0:0:0:0 | (unnamed parameter 0) | file://:0:0:0:0 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | file://:0:0:0:0 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | file://:0:0:0:0 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | stl.h:75:8:75:8 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | stl.h:75:8:75:8 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | stl.h:389:9:389:9 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | stl.h:389:9:389:9 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | stl.h:389:9:389:9 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | stl.h:389:9:389:9 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | stl.h:389:9:389:9 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | stl.h:389:9:389:9 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | stl.h:389:9:389:9 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | stl.h:389:9:389:9 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | stl.h:389:9:389:9 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | stl.h:389:9:389:9 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | structlikeclass.cpp:5:7:5:7 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | structlikeclass.cpp:5:7:5:7 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | taint.cpp:228:11:228:11 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | taint.cpp:228:11:228:11 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | taint.cpp:235:11:235:11 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | taint.cpp:235:11:235:11 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | taint.cpp:235:11:235:11 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | taint.cpp:243:11:243:11 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | taint.cpp:243:11:243:11 | (unnamed parameter 0) | |
| format.cpp:16:21:16:21 | s | format.cpp:16:21:16:21 | s | |
| format.cpp:16:21:16:21 | s | format.cpp:22:22:22:22 | s | |
| format.cpp:16:31:16:31 | n | format.cpp:22:25:22:25 | n | |
@@ -3554,8 +3577,10 @@
| standalone_iterators.cpp:120:2:120:3 | it | standalone_iterators.cpp:120:5:120:5 | call to operator+= | TAINT |
| standalone_iterators.cpp:120:2:120:3 | ref arg it | standalone_iterators.cpp:121:7:121:8 | it | |
| standalone_iterators.cpp:120:8:120:13 | call to source | standalone_iterators.cpp:120:2:120:3 | ref arg it | TAINT |
| stl.h:75:8:75:8 | Unknown literal | stl.h:75:8:75:8 | constructor init of field container | TAINT |
| stl.h:75:8:75:8 | Unknown literal | stl.h:75:8:75:8 | constructor init of field container | TAINT |
| stl.h:75:8:75:8 | container | stl.h:75:8:75:8 | constructor init of field container | TAINT |
| stl.h:75:8:75:8 | container | stl.h:75:8:75:8 | constructor init of field container | TAINT |
| stl.h:75:8:75:8 | container | stl.h:75:8:75:8 | container | |
| stl.h:75:8:75:8 | container | stl.h:75:8:75:8 | container | |
| stl.h:75:8:75:8 | this | stl.h:75:8:75:8 | constructor init of field container [pre-this] | |
| stl.h:75:8:75:8 | this | stl.h:75:8:75:8 | constructor init of field container [pre-this] | |
| stl.h:95:69:95:69 | x | stl.h:95:69:95:69 | x | |
@@ -3573,16 +3598,6 @@
| stl.h:292:30:292:40 | call to allocator | stl.h:292:21:292:41 | noexcept(...) | TAINT |
| stl.h:292:30:292:40 | call to allocator | stl.h:292:21:292:41 | noexcept(...) | TAINT |
| stl.h:292:53:292:63 | 0 | stl.h:292:46:292:64 | (no string representation) | TAINT |
| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field first | TAINT |
| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field first | TAINT |
| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field first | TAINT |
| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field first | TAINT |
| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field first | TAINT |
| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field second | TAINT |
| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field second | TAINT |
| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field second | TAINT |
| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field second | TAINT |
| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field second | TAINT |
| stl.h:389:9:389:9 | constructor init of field first [post-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | |
| stl.h:389:9:389:9 | constructor init of field first [post-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | |
| stl.h:389:9:389:9 | constructor init of field first [post-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | |
@@ -3593,6 +3608,26 @@
| stl.h:389:9:389:9 | constructor init of field first [pre-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | |
| stl.h:389:9:389:9 | constructor init of field first [pre-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | |
| stl.h:389:9:389:9 | constructor init of field first [pre-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | |
| stl.h:389:9:389:9 | first | stl.h:389:9:389:9 | constructor init of field first | TAINT |
| stl.h:389:9:389:9 | first | stl.h:389:9:389:9 | constructor init of field first | TAINT |
| stl.h:389:9:389:9 | first | stl.h:389:9:389:9 | constructor init of field first | TAINT |
| stl.h:389:9:389:9 | first | stl.h:389:9:389:9 | constructor init of field first | TAINT |
| stl.h:389:9:389:9 | first | stl.h:389:9:389:9 | constructor init of field first | TAINT |
| stl.h:389:9:389:9 | first | stl.h:389:9:389:9 | first | |
| stl.h:389:9:389:9 | first | stl.h:389:9:389:9 | first | |
| stl.h:389:9:389:9 | first | stl.h:389:9:389:9 | first | |
| stl.h:389:9:389:9 | first | stl.h:389:9:389:9 | first | |
| stl.h:389:9:389:9 | first | stl.h:389:9:389:9 | first | |
| stl.h:389:9:389:9 | second | stl.h:389:9:389:9 | constructor init of field second | TAINT |
| stl.h:389:9:389:9 | second | stl.h:389:9:389:9 | constructor init of field second | TAINT |
| stl.h:389:9:389:9 | second | stl.h:389:9:389:9 | constructor init of field second | TAINT |
| stl.h:389:9:389:9 | second | stl.h:389:9:389:9 | constructor init of field second | TAINT |
| stl.h:389:9:389:9 | second | stl.h:389:9:389:9 | constructor init of field second | TAINT |
| stl.h:389:9:389:9 | second | stl.h:389:9:389:9 | second | |
| stl.h:389:9:389:9 | second | stl.h:389:9:389:9 | second | |
| stl.h:389:9:389:9 | second | stl.h:389:9:389:9 | second | |
| stl.h:389:9:389:9 | second | stl.h:389:9:389:9 | second | |
| stl.h:389:9:389:9 | second | stl.h:389:9:389:9 | second | |
| stl.h:389:9:389:9 | this | stl.h:389:9:389:9 | constructor init of field first [pre-this] | |
| stl.h:389:9:389:9 | this | stl.h:389:9:389:9 | constructor init of field first [pre-this] | |
| stl.h:389:9:389:9 | this | stl.h:389:9:389:9 | constructor init of field first [pre-this] | |
@@ -5354,10 +5389,12 @@
| stringstream.cpp:266:54:266:58 | ref arg call to flush | stringstream.cpp:266:35:266:39 | ref arg call to write | TAINT |
| stringstream.cpp:266:68:266:72 | xyz | stringstream.cpp:266:54:266:58 | ref arg call to flush | TAINT |
| stringstream.cpp:266:68:266:72 | xyz | stringstream.cpp:266:62:266:66 | call to write | TAINT |
| structlikeclass.cpp:5:7:5:7 | Unknown literal | structlikeclass.cpp:5:7:5:7 | constructor init of field v | TAINT |
| structlikeclass.cpp:5:7:5:7 | Unknown literal | structlikeclass.cpp:5:7:5:7 | constructor init of field v | TAINT |
| structlikeclass.cpp:5:7:5:7 | this | structlikeclass.cpp:5:7:5:7 | constructor init of field v [pre-this] | |
| structlikeclass.cpp:5:7:5:7 | this | structlikeclass.cpp:5:7:5:7 | constructor init of field v [pre-this] | |
| structlikeclass.cpp:5:7:5:7 | v | structlikeclass.cpp:5:7:5:7 | constructor init of field v | TAINT |
| structlikeclass.cpp:5:7:5:7 | v | structlikeclass.cpp:5:7:5:7 | constructor init of field v | TAINT |
| structlikeclass.cpp:5:7:5:7 | v | structlikeclass.cpp:5:7:5:7 | v | |
| structlikeclass.cpp:5:7:5:7 | v | structlikeclass.cpp:5:7:5:7 | v | |
| structlikeclass.cpp:8:2:8:16 | this | structlikeclass.cpp:8:28:8:32 | constructor init of field v [pre-this] | |
| structlikeclass.cpp:8:22:8:23 | _v | structlikeclass.cpp:8:30:8:31 | _v | |
| structlikeclass.cpp:8:30:8:31 | _v | structlikeclass.cpp:8:28:8:32 | constructor init of field v | TAINT |
@@ -5973,24 +6010,29 @@
| taint.cpp:226:9:226:10 | 0 | taint.cpp:261:7:261:7 | w | |
| taint.cpp:228:10:232:2 | [...](...){...} | taint.cpp:233:7:233:7 | a | |
| taint.cpp:228:10:232:2 | {...} | taint.cpp:228:10:232:2 | [...](...){...} | |
| taint.cpp:228:11:228:11 | Unknown literal | taint.cpp:228:11:228:11 | constructor init of field t | TAINT |
| taint.cpp:228:11:228:11 | Unknown literal | taint.cpp:228:11:228:11 | constructor init of field u | TAINT |
| taint.cpp:228:11:228:11 | constructor init of field t [post-this] | taint.cpp:228:11:228:11 | constructor init of field u [pre-this] | |
| taint.cpp:228:11:228:11 | constructor init of field t [pre-this] | taint.cpp:228:11:228:11 | constructor init of field u [pre-this] | |
| taint.cpp:228:11:228:11 | t | taint.cpp:228:11:228:11 | constructor init of field t | TAINT |
| taint.cpp:228:11:228:11 | t | taint.cpp:228:11:228:11 | t | |
| taint.cpp:228:11:228:11 | this | taint.cpp:228:11:228:11 | constructor init of field t [pre-this] | |
| taint.cpp:228:11:228:11 | u | taint.cpp:228:11:228:11 | constructor init of field u | TAINT |
| taint.cpp:228:11:228:11 | u | taint.cpp:228:11:228:11 | u | |
| taint.cpp:228:17:228:17 | this | taint.cpp:229:3:229:6 | this | |
| taint.cpp:229:3:229:6 | this | taint.cpp:230:3:230:6 | this | |
| taint.cpp:230:3:230:6 | this | file://:0:0:0:0 | this | |
| taint.cpp:235:10:239:2 | [...](...){...} | taint.cpp:240:2:240:2 | b | |
| taint.cpp:235:10:239:2 | {...} | taint.cpp:235:10:239:2 | [...](...){...} | |
| taint.cpp:235:11:235:11 | Unknown literal | taint.cpp:235:11:235:11 | constructor init of field t | TAINT |
| taint.cpp:235:11:235:11 | Unknown literal | taint.cpp:235:11:235:11 | constructor init of field u | TAINT |
| taint.cpp:235:11:235:11 | Unknown literal | taint.cpp:235:11:235:11 | constructor init of field v | TAINT |
| taint.cpp:235:11:235:11 | constructor init of field t [post-this] | taint.cpp:235:11:235:11 | constructor init of field u [pre-this] | |
| taint.cpp:235:11:235:11 | constructor init of field t [pre-this] | taint.cpp:235:11:235:11 | constructor init of field u [pre-this] | |
| taint.cpp:235:11:235:11 | constructor init of field u [post-this] | taint.cpp:235:11:235:11 | constructor init of field v [pre-this] | |
| taint.cpp:235:11:235:11 | constructor init of field u [pre-this] | taint.cpp:235:11:235:11 | constructor init of field v [pre-this] | |
| taint.cpp:235:11:235:11 | t | taint.cpp:235:11:235:11 | constructor init of field t | TAINT |
| taint.cpp:235:11:235:11 | t | taint.cpp:235:11:235:11 | t | |
| taint.cpp:235:11:235:11 | this | taint.cpp:235:11:235:11 | constructor init of field t [pre-this] | |
| taint.cpp:235:11:235:11 | u | taint.cpp:235:11:235:11 | constructor init of field u | TAINT |
| taint.cpp:235:11:235:11 | u | taint.cpp:235:11:235:11 | u | |
| taint.cpp:235:11:235:11 | v | taint.cpp:235:11:235:11 | constructor init of field v | TAINT |
| taint.cpp:235:11:235:11 | v | taint.cpp:235:11:235:11 | v | |
| taint.cpp:235:15:235:15 | this | taint.cpp:236:3:236:6 | this | |
| taint.cpp:236:3:236:6 | this | taint.cpp:237:3:237:6 | this | |
| taint.cpp:237:3:237:6 | this | taint.cpp:238:3:238:14 | this | |
@@ -5998,11 +6040,13 @@
| taint.cpp:238:7:238:12 | call to source | taint.cpp:238:3:238:14 | ... = ... | |
| taint.cpp:243:10:246:2 | [...](...){...} | taint.cpp:247:2:247:2 | c | |
| taint.cpp:243:10:246:2 | {...} | taint.cpp:243:10:246:2 | [...](...){...} | |
| taint.cpp:243:11:243:11 | Unknown literal | taint.cpp:243:11:243:11 | constructor init of field t | TAINT |
| taint.cpp:243:11:243:11 | Unknown literal | taint.cpp:243:11:243:11 | constructor init of field u | TAINT |
| taint.cpp:243:11:243:11 | constructor init of field t [post-this] | taint.cpp:243:11:243:11 | constructor init of field u [pre-this] | |
| taint.cpp:243:11:243:11 | constructor init of field t [pre-this] | taint.cpp:243:11:243:11 | constructor init of field u [pre-this] | |
| taint.cpp:243:11:243:11 | t | taint.cpp:243:11:243:11 | constructor init of field t | TAINT |
| taint.cpp:243:11:243:11 | t | taint.cpp:243:11:243:11 | t | |
| taint.cpp:243:11:243:11 | this | taint.cpp:243:11:243:11 | constructor init of field t [pre-this] | |
| taint.cpp:243:11:243:11 | u | taint.cpp:243:11:243:11 | constructor init of field u | TAINT |
| taint.cpp:243:11:243:11 | u | taint.cpp:243:11:243:11 | u | |
| taint.cpp:243:15:243:15 | this | taint.cpp:244:3:244:6 | this | |
| taint.cpp:244:3:244:6 | this | taint.cpp:245:3:245:6 | this | |
| taint.cpp:249:11:252:2 | [...](...){...} | taint.cpp:253:2:253:2 | d | |

View File

@@ -12,8 +12,12 @@
| addressOf.cpp:40:15:40:15 | i | non-const address |
| addressOf.cpp:42:19:42:22 | iref | non-const address |
| addressOf.cpp:47:12:47:31 | captured | non-const address |
| addressOf.cpp:47:13:47:13 | (unnamed parameter 0) | |
| addressOf.cpp:47:13:47:13 | captured | |
| addressOf.cpp:47:19:47:28 | captured | |
| addressOf.cpp:48:3:48:4 | f1 | const address |
| addressOf.cpp:49:13:49:13 | (unnamed parameter 0) | |
| addressOf.cpp:49:13:49:13 | captured | |
| addressOf.cpp:49:15:49:22 | captured | non-const address |
| addressOf.cpp:49:27:49:36 | captured | |
| addressOf.cpp:50:3:50:4 | f2 | const address |
@@ -245,6 +249,10 @@
| test.cpp:173:19:173:19 | x | const address |
| test.cpp:174:20:174:20 | x | const address |
| test.cpp:175:7:175:7 | x | |
| test.cpp:178:8:178:8 | (unnamed parameter 0) | |
| test.cpp:178:8:178:8 | (unnamed parameter 0) | |
| test.cpp:178:8:178:8 | nested | |
| test.cpp:178:8:178:8 | x_ | |
| test.cpp:183:38:183:41 | yptr | |
| test.cpp:183:48:183:48 | z | |
| test.cpp:184:28:184:35 | static_y | non-const address |

View File

@@ -8,6 +8,10 @@
| addressOf.cpp:61:33:61:35 | ref | addressOf.cpp:63:24:63:26 | ref |
| addressOf.cpp:70:29:70:31 | obj | addressOf.cpp:71:32:71:34 | obj |
| addressOf.cpp:70:29:70:31 | obj | addressOf.cpp:71:32:71:34 | obj |
| file://:0:0:0:0 | (unnamed parameter 0) | addressOf.cpp:47:13:47:13 | (unnamed parameter 0) |
| file://:0:0:0:0 | (unnamed parameter 0) | addressOf.cpp:49:13:49:13 | (unnamed parameter 0) |
| file://:0:0:0:0 | (unnamed parameter 0) | test.cpp:178:8:178:8 | (unnamed parameter 0) |
| file://:0:0:0:0 | (unnamed parameter 0) | test.cpp:178:8:178:8 | (unnamed parameter 0) |
| indirect_use.cpp:19:31:19:32 | ip | indirect_use.cpp:20:14:20:15 | ip |
| indirect_use.cpp:24:31:24:32 | ip | indirect_use.cpp:25:14:25:15 | ip |
| indirect_use.cpp:30:28:30:30 | ppp | indirect_use.cpp:31:19:31:21 | ppp |

View File

@@ -26,6 +26,10 @@
| addressOf.cpp:70:29:70:31 | obj | addressOf.cpp:71:32:71:34 | obj |
| addressOf.cpp:76:7:76:7 | x | addressOf.cpp:77:27:77:27 | x |
| addressOf.cpp:76:7:76:7 | x | addressOf.cpp:77:48:77:48 | x |
| file://:0:0:0:0 | (unnamed parameter 0) | addressOf.cpp:47:13:47:13 | (unnamed parameter 0) |
| file://:0:0:0:0 | (unnamed parameter 0) | addressOf.cpp:49:13:49:13 | (unnamed parameter 0) |
| file://:0:0:0:0 | (unnamed parameter 0) | test.cpp:178:8:178:8 | (unnamed parameter 0) |
| file://:0:0:0:0 | (unnamed parameter 0) | test.cpp:178:8:178:8 | (unnamed parameter 0) |
| indirect_use.cpp:19:31:19:32 | ip | indirect_use.cpp:20:14:20:15 | ip |
| indirect_use.cpp:20:10:20:10 | p | indirect_use.cpp:21:17:21:17 | p |
| indirect_use.cpp:24:31:24:32 | ip | indirect_use.cpp:25:14:25:15 | ip |

View File

@@ -12,6 +12,10 @@
| addressOf.cpp:61:23:61:25 | ptr | addressOf.cpp:63:19:63:21 | ptr |
| addressOf.cpp:70:29:70:31 | obj | addressOf.cpp:71:32:71:34 | obj |
| addressOf.cpp:76:7:76:7 | x | addressOf.cpp:77:48:77:48 | x |
| file://:0:0:0:0 | (unnamed parameter 0) | addressOf.cpp:47:13:47:13 | (unnamed parameter 0) |
| file://:0:0:0:0 | (unnamed parameter 0) | addressOf.cpp:49:13:49:13 | (unnamed parameter 0) |
| file://:0:0:0:0 | (unnamed parameter 0) | test.cpp:178:8:178:8 | (unnamed parameter 0) |
| file://:0:0:0:0 | (unnamed parameter 0) | test.cpp:178:8:178:8 | (unnamed parameter 0) |
| indirect_use.cpp:19:31:19:32 | ip | indirect_use.cpp:20:14:20:15 | ip |
| indirect_use.cpp:20:10:20:10 | p | indirect_use.cpp:21:17:21:17 | p |
| indirect_use.cpp:24:31:24:32 | ip | indirect_use.cpp:25:14:25:15 | ip |

View File

@@ -155,15 +155,21 @@ bad_asts.cpp:
# 19| getInitializer(0): [ConstructorFieldInit] constructor init of field x
# 19| Type = [IntType] int
# 19| ValueCategory = prvalue
# 19| getExpr(): [Literal] Unknown literal
# 19| getExpr(): [FieldAccess] x
# 19| Type = [IntType] int
# 19| ValueCategory = prvalue
# 19| ValueCategory = prvalue(load)
# 19| getQualifier(): [VariableAccess] (unnamed parameter 0)
# 19| Type = [LValueReferenceType] const Point &
# 19| ValueCategory = prvalue(load)
# 19| getInitializer(1): [ConstructorFieldInit] constructor init of field y
# 19| Type = [IntType] int
# 19| ValueCategory = prvalue
# 19| getExpr(): [Literal] Unknown literal
# 19| getExpr(): [FieldAccess] y
# 19| Type = [IntType] int
# 19| ValueCategory = prvalue
# 19| ValueCategory = prvalue(load)
# 19| getQualifier(): [VariableAccess] (unnamed parameter 0)
# 19| Type = [LValueReferenceType] const Point &
# 19| ValueCategory = prvalue(load)
# 19| getEntryPoint(): [BlockStmt] { ... }
# 19| getStmt(0): [ReturnStmt] return ...
# 19| [MoveConstructor] void Bad::Point::Point(Bad::Point&&)
@@ -11645,51 +11651,75 @@ ir.cpp:
# 1486| getInitializer(0): [ConstructorFieldInit] constructor init of field i
# 1486| Type = [IntType] int
# 1486| ValueCategory = prvalue
# 1486| getExpr(): [Literal] Unknown literal
# 1486| getExpr(): [FieldAccess] i
# 1486| Type = [IntType] int
# 1486| ValueCategory = prvalue
# 1486| ValueCategory = prvalue(load)
# 1486| getQualifier(): [VariableAccess] (unnamed parameter 0)
# 1486| Type = [LValueReferenceType] const StructuredBindingDataMemberStruct &
# 1486| ValueCategory = prvalue(load)
# 1486| getInitializer(1): [ConstructorFieldInit] constructor init of field d
# 1486| Type = [DoubleType] double
# 1486| ValueCategory = prvalue
# 1486| getExpr(): [Literal] Unknown literal
# 1486| getExpr(): [FieldAccess] d
# 1486| Type = [DoubleType] double
# 1486| ValueCategory = prvalue
# 1486| ValueCategory = prvalue(load)
# 1486| getQualifier(): [VariableAccess] (unnamed parameter 0)
# 1486| Type = [LValueReferenceType] const StructuredBindingDataMemberStruct &
# 1486| ValueCategory = prvalue(load)
# 1486| getInitializer(2): [ConstructorFieldInit] constructor init of field b
# 1486| Type = [IntType] unsigned int
# 1486| ValueCategory = prvalue
# 1486| getExpr(): [Literal] Unknown literal
# 1486| getExpr(): [FieldAccess] b
# 1486| Type = [IntType] unsigned int
# 1486| ValueCategory = prvalue
# 1486| ValueCategory = prvalue(load)
# 1486| getQualifier(): [VariableAccess] (unnamed parameter 0)
# 1486| Type = [LValueReferenceType] const StructuredBindingDataMemberStruct &
# 1486| ValueCategory = prvalue(load)
# 1486| getInitializer(3): [ConstructorFieldInit] constructor init of field r
# 1486| Type = [LValueReferenceType] int &
# 1486| ValueCategory = prvalue
# 1486| getExpr(): [Literal] Unknown literal
# 1486| getExpr(): [FieldAccess] r
# 1486| Type = [LValueReferenceType] int &
# 1486| ValueCategory = prvalue
# 1486| ValueCategory = prvalue(load)
# 1486| getQualifier(): [VariableAccess] (unnamed parameter 0)
# 1486| Type = [LValueReferenceType] const StructuredBindingDataMemberStruct &
# 1486| ValueCategory = prvalue(load)
# 1486| getInitializer(4): [ConstructorFieldInit] constructor init of field p
# 1486| Type = [IntPointerType] int *
# 1486| ValueCategory = prvalue
# 1486| getExpr(): [Literal] Unknown literal
# 1486| getExpr(): [FieldAccess] p
# 1486| Type = [IntPointerType] int *
# 1486| ValueCategory = prvalue
# 1486| ValueCategory = prvalue(load)
# 1486| getQualifier(): [VariableAccess] (unnamed parameter 0)
# 1486| Type = [LValueReferenceType] const StructuredBindingDataMemberStruct &
# 1486| ValueCategory = prvalue(load)
# 1486| getInitializer(5): [ConstructorFieldInit] constructor init of field xs
# 1486| Type = [CTypedefType,NestedTypedefType] ArrayType
# 1486| ValueCategory = prvalue
# 1486| getExpr(): [Literal] Unknown literal
# 1486| getExpr(): [FieldAccess] xs
# 1486| Type = [CTypedefType,NestedTypedefType] ArrayType
# 1486| ValueCategory = prvalue
# 1486| ValueCategory = prvalue(load)
# 1486| getQualifier(): [VariableAccess] (unnamed parameter 0)
# 1486| Type = [LValueReferenceType] const StructuredBindingDataMemberStruct &
# 1486| ValueCategory = prvalue(load)
# 1486| getInitializer(6): [ConstructorFieldInit] constructor init of field r_alt
# 1486| Type = [CTypedefType,NestedTypedefType] RefType
# 1486| ValueCategory = prvalue
# 1486| getExpr(): [Literal] Unknown literal
# 1486| getExpr(): [FieldAccess] r_alt
# 1486| Type = [CTypedefType,NestedTypedefType] RefType
# 1486| ValueCategory = prvalue
# 1486| ValueCategory = prvalue(load)
# 1486| getQualifier(): [VariableAccess] (unnamed parameter 0)
# 1486| Type = [LValueReferenceType] const StructuredBindingDataMemberStruct &
# 1486| ValueCategory = prvalue(load)
# 1486| getInitializer(7): [ConstructorFieldInit] constructor init of field m
# 1486| Type = [Struct] StructuredBindingDataMemberMemberStruct
# 1486| ValueCategory = prvalue
# 1486| getExpr(): [Literal] Unknown literal
# 1486| getExpr(): [FieldAccess] m
# 1486| Type = [Struct] StructuredBindingDataMemberMemberStruct
# 1486| ValueCategory = prvalue
# 1486| ValueCategory = prvalue(load)
# 1486| getQualifier(): [VariableAccess] (unnamed parameter 0)
# 1486| Type = [LValueReferenceType] const StructuredBindingDataMemberStruct &
# 1486| ValueCategory = prvalue(load)
# 1486| getEntryPoint(): [BlockStmt] { ... }
# 1486| getStmt(0): [ReturnStmt] return ...
# 1486| [MoveConstructor] void StructuredBindingDataMemberStruct::StructuredBindingDataMemberStruct(StructuredBindingDataMemberStruct&&)
@@ -12066,21 +12096,30 @@ ir.cpp:
# 1539| getInitializer(0): [ConstructorFieldInit] constructor init of field i
# 1539| Type = [IntType] int
# 1539| ValueCategory = prvalue
# 1539| getExpr(): [Literal] Unknown literal
# 1539| getExpr(): [FieldAccess] i
# 1539| Type = [IntType] int
# 1539| ValueCategory = prvalue
# 1539| ValueCategory = prvalue(load)
# 1539| getQualifier(): [VariableAccess] (unnamed parameter 0)
# 1539| Type = [LValueReferenceType] const StructuredBindingTupleRefGet &
# 1539| ValueCategory = prvalue(load)
# 1539| getInitializer(1): [ConstructorFieldInit] constructor init of field d
# 1539| Type = [DoubleType] double
# 1539| ValueCategory = prvalue
# 1539| getExpr(): [Literal] Unknown literal
# 1539| getExpr(): [FieldAccess] d
# 1539| Type = [DoubleType] double
# 1539| ValueCategory = prvalue
# 1539| ValueCategory = prvalue(load)
# 1539| getQualifier(): [VariableAccess] (unnamed parameter 0)
# 1539| Type = [LValueReferenceType] const StructuredBindingTupleRefGet &
# 1539| ValueCategory = prvalue(load)
# 1539| getInitializer(2): [ConstructorFieldInit] constructor init of field r
# 1539| Type = [LValueReferenceType] int &
# 1539| ValueCategory = prvalue
# 1539| getExpr(): [Literal] Unknown literal
# 1539| getExpr(): [FieldAccess] r
# 1539| Type = [LValueReferenceType] int &
# 1539| ValueCategory = prvalue
# 1539| ValueCategory = prvalue(load)
# 1539| getQualifier(): [VariableAccess] (unnamed parameter 0)
# 1539| Type = [LValueReferenceType] const StructuredBindingTupleRefGet &
# 1539| ValueCategory = prvalue(load)
# 1539| getEntryPoint(): [BlockStmt] { ... }
# 1539| getStmt(0): [ReturnStmt] return ...
# 1539| [MoveConstructor] void StructuredBindingTupleRefGet::StructuredBindingTupleRefGet(StructuredBindingTupleRefGet&&)

View File

@@ -41,6 +41,35 @@
| bad_asts.cpp:16:7:16:23 | ChiTotal | total:m14_4 |
| bad_asts.cpp:16:7:16:23 | SideEffect | ~m14_4 |
| bad_asts.cpp:16:25:16:25 | Arg(0) | 0:r16_3 |
| bad_asts.cpp:19:10:19:10 | Address | &:r19_5 |
| bad_asts.cpp:19:10:19:10 | Address | &:r19_5 |
| bad_asts.cpp:19:10:19:10 | Address | &:r19_7 |
| bad_asts.cpp:19:10:19:10 | Address | &:r19_7 |
| bad_asts.cpp:19:10:19:10 | Address | &:r19_9 |
| bad_asts.cpp:19:10:19:10 | Address | &:r19_10 |
| bad_asts.cpp:19:10:19:10 | Address | &:r19_12 |
| bad_asts.cpp:19:10:19:10 | Address | &:r19_16 |
| bad_asts.cpp:19:10:19:10 | Address | &:r19_17 |
| bad_asts.cpp:19:10:19:10 | Address | &:r19_19 |
| bad_asts.cpp:19:10:19:10 | ChiPartial | partial:m19_3 |
| bad_asts.cpp:19:10:19:10 | ChiPartial | partial:m19_14 |
| bad_asts.cpp:19:10:19:10 | ChiPartial | partial:m19_21 |
| bad_asts.cpp:19:10:19:10 | ChiTotal | total:m19_2 |
| bad_asts.cpp:19:10:19:10 | ChiTotal | total:m19_8 |
| bad_asts.cpp:19:10:19:10 | ChiTotal | total:m19_15 |
| bad_asts.cpp:19:10:19:10 | Load | m0_2 |
| bad_asts.cpp:19:10:19:10 | Load | m0_2 |
| bad_asts.cpp:19:10:19:10 | Load | m19_6 |
| bad_asts.cpp:19:10:19:10 | Load | ~m0_4 |
| bad_asts.cpp:19:10:19:10 | Load | ~m0_4 |
| bad_asts.cpp:19:10:19:10 | SideEffect | m19_3 |
| bad_asts.cpp:19:10:19:10 | SideEffect | m19_22 |
| bad_asts.cpp:19:10:19:10 | StoreValue | r19_13 |
| bad_asts.cpp:19:10:19:10 | StoreValue | r19_20 |
| bad_asts.cpp:19:10:19:10 | Unary | m19_6 |
| bad_asts.cpp:19:10:19:10 | Unary | m19_6 |
| bad_asts.cpp:19:10:19:10 | Unary | r19_11 |
| bad_asts.cpp:19:10:19:10 | Unary | r19_18 |
| bad_asts.cpp:22:5:22:9 | Address | &:r22_5 |
| bad_asts.cpp:22:5:22:9 | Address | &:r22_5 |
| bad_asts.cpp:22:5:22:9 | Address | &:r22_7 |
@@ -633,6 +662,12 @@
| file://:0:0:0:0 | Address | &:r0_1 |
| file://:0:0:0:0 | Address | &:r0_1 |
| file://:0:0:0:0 | Address | &:r0_1 |
| file://:0:0:0:0 | Address | &:r0_1 |
| file://:0:0:0:0 | Address | &:r0_1 |
| file://:0:0:0:0 | Address | &:r0_1 |
| file://:0:0:0:0 | Address | &:r0_1 |
| file://:0:0:0:0 | Address | &:r0_1 |
| file://:0:0:0:0 | Address | &:r0_1 |
| file://:0:0:0:0 | Address | &:r0_2 |
| file://:0:0:0:0 | Address | &:r0_3 |
| file://:0:0:0:0 | Address | &:r0_3 |
@@ -643,6 +678,12 @@
| file://:0:0:0:0 | Address | &:r0_3 |
| file://:0:0:0:0 | Address | &:r0_3 |
| file://:0:0:0:0 | Address | &:r0_3 |
| file://:0:0:0:0 | Address | &:r0_3 |
| file://:0:0:0:0 | Address | &:r0_3 |
| file://:0:0:0:0 | Address | &:r0_3 |
| file://:0:0:0:0 | Address | &:r0_3 |
| file://:0:0:0:0 | Address | &:r0_3 |
| file://:0:0:0:0 | Address | &:r0_3 |
| file://:0:0:0:0 | Address | &:r0_5 |
| file://:0:0:0:0 | Address | &:r0_5 |
| file://:0:0:0:0 | Address | &:r0_5 |
@@ -692,6 +733,9 @@
| file://:0:0:0:0 | Load | m0_2 |
| file://:0:0:0:0 | Load | m0_2 |
| file://:0:0:0:0 | Load | m0_2 |
| file://:0:0:0:0 | Load | m0_2 |
| file://:0:0:0:0 | Load | m0_2 |
| file://:0:0:0:0 | Load | m0_2 |
| file://:0:0:0:0 | Load | m745_6 |
| file://:0:0:0:0 | Load | m754_6 |
| file://:0:0:0:0 | Load | m763_6 |
@@ -706,6 +750,9 @@
| file://:0:0:0:0 | SideEffect | m0_4 |
| file://:0:0:0:0 | SideEffect | m0_4 |
| file://:0:0:0:0 | SideEffect | m0_4 |
| file://:0:0:0:0 | SideEffect | m0_4 |
| file://:0:0:0:0 | SideEffect | m0_4 |
| file://:0:0:0:0 | SideEffect | m0_4 |
| file://:0:0:0:0 | SideEffect | m1078_23 |
| file://:0:0:0:0 | SideEffect | m1078_23 |
| file://:0:0:0:0 | SideEffect | m1084_23 |
@@ -6769,13 +6816,102 @@
| ir.cpp:1482:8:1482:8 | SideEffect | m1482_8 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_5 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_5 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_5 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_5 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_7 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_7 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_7 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_7 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_9 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_10 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_12 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_16 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_17 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_19 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_23 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_24 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_26 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_30 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_31 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_33 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_37 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_38 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_40 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_44 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_45 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_47 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_51 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_52 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_54 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_58 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_59 |
| ir.cpp:1486:8:1486:8 | Address | &:r1486_61 |
| ir.cpp:1486:8:1486:8 | ChiPartial | partial:m1486_3 |
| ir.cpp:1486:8:1486:8 | ChiPartial | partial:m1486_3 |
| ir.cpp:1486:8:1486:8 | ChiPartial | partial:m1486_14 |
| ir.cpp:1486:8:1486:8 | ChiPartial | partial:m1486_21 |
| ir.cpp:1486:8:1486:8 | ChiPartial | partial:m1486_28 |
| ir.cpp:1486:8:1486:8 | ChiPartial | partial:m1486_35 |
| ir.cpp:1486:8:1486:8 | ChiPartial | partial:m1486_42 |
| ir.cpp:1486:8:1486:8 | ChiPartial | partial:m1486_49 |
| ir.cpp:1486:8:1486:8 | ChiPartial | partial:m1486_56 |
| ir.cpp:1486:8:1486:8 | ChiPartial | partial:m1486_63 |
| ir.cpp:1486:8:1486:8 | ChiTotal | total:m1486_2 |
| ir.cpp:1486:8:1486:8 | ChiTotal | total:m1486_2 |
| ir.cpp:1486:8:1486:8 | ChiTotal | total:m1486_8 |
| ir.cpp:1486:8:1486:8 | ChiTotal | total:m1486_15 |
| ir.cpp:1486:8:1486:8 | ChiTotal | total:m1486_22 |
| ir.cpp:1486:8:1486:8 | ChiTotal | total:m1486_29 |
| ir.cpp:1486:8:1486:8 | ChiTotal | total:m1486_36 |
| ir.cpp:1486:8:1486:8 | ChiTotal | total:m1486_43 |
| ir.cpp:1486:8:1486:8 | ChiTotal | total:m1486_50 |
| ir.cpp:1486:8:1486:8 | ChiTotal | total:m1486_57 |
| ir.cpp:1486:8:1486:8 | Load | m0_2 |
| ir.cpp:1486:8:1486:8 | Load | m0_2 |
| ir.cpp:1486:8:1486:8 | Load | m0_2 |
| ir.cpp:1486:8:1486:8 | Load | m0_2 |
| ir.cpp:1486:8:1486:8 | Load | m0_2 |
| ir.cpp:1486:8:1486:8 | Load | m0_2 |
| ir.cpp:1486:8:1486:8 | Load | m0_2 |
| ir.cpp:1486:8:1486:8 | Load | m0_2 |
| ir.cpp:1486:8:1486:8 | Load | m1486_6 |
| ir.cpp:1486:8:1486:8 | Load | m1486_6 |
| ir.cpp:1486:8:1486:8 | Load | ~m0_4 |
| ir.cpp:1486:8:1486:8 | Load | ~m0_4 |
| ir.cpp:1486:8:1486:8 | Load | ~m0_4 |
| ir.cpp:1486:8:1486:8 | Load | ~m0_4 |
| ir.cpp:1486:8:1486:8 | Load | ~m0_4 |
| ir.cpp:1486:8:1486:8 | Load | ~m0_4 |
| ir.cpp:1486:8:1486:8 | Load | ~m0_4 |
| ir.cpp:1486:8:1486:8 | Load | ~m0_4 |
| ir.cpp:1486:8:1486:8 | SideEffect | m1486_3 |
| ir.cpp:1486:8:1486:8 | SideEffect | m1486_3 |
| ir.cpp:1486:8:1486:8 | SideEffect | m1486_8 |
| ir.cpp:1486:8:1486:8 | SideEffect | m1486_64 |
| ir.cpp:1486:8:1486:8 | StoreValue | r1486_13 |
| ir.cpp:1486:8:1486:8 | StoreValue | r1486_20 |
| ir.cpp:1486:8:1486:8 | StoreValue | r1486_27 |
| ir.cpp:1486:8:1486:8 | StoreValue | r1486_34 |
| ir.cpp:1486:8:1486:8 | StoreValue | r1486_41 |
| ir.cpp:1486:8:1486:8 | StoreValue | r1486_48 |
| ir.cpp:1486:8:1486:8 | StoreValue | r1486_55 |
| ir.cpp:1486:8:1486:8 | StoreValue | r1486_62 |
| ir.cpp:1486:8:1486:8 | Unary | m1486_6 |
| ir.cpp:1486:8:1486:8 | Unary | m1486_6 |
| ir.cpp:1486:8:1486:8 | Unary | m1486_6 |
| ir.cpp:1486:8:1486:8 | Unary | m1486_6 |
| ir.cpp:1486:8:1486:8 | Unary | m1486_6 |
| ir.cpp:1486:8:1486:8 | Unary | m1486_6 |
| ir.cpp:1486:8:1486:8 | Unary | m1486_6 |
| ir.cpp:1486:8:1486:8 | Unary | m1486_6 |
| ir.cpp:1486:8:1486:8 | Unary | r1486_11 |
| ir.cpp:1486:8:1486:8 | Unary | r1486_18 |
| ir.cpp:1486:8:1486:8 | Unary | r1486_25 |
| ir.cpp:1486:8:1486:8 | Unary | r1486_32 |
| ir.cpp:1486:8:1486:8 | Unary | r1486_39 |
| ir.cpp:1486:8:1486:8 | Unary | r1486_46 |
| ir.cpp:1486:8:1486:8 | Unary | r1486_53 |
| ir.cpp:1486:8:1486:8 | Unary | r1486_60 |
| ir.cpp:1499:6:1499:35 | ChiPartial | partial:m1499_3 |
| ir.cpp:1499:6:1499:35 | ChiTotal | total:m1499_2 |
| ir.cpp:1499:6:1499:35 | SideEffect | ~m1525_7 |
@@ -6948,13 +7084,52 @@
| ir.cpp:1528:17:1528:17 | StoreValue | r1528_4 |
| ir.cpp:1539:8:1539:8 | Address | &:r1539_5 |
| ir.cpp:1539:8:1539:8 | Address | &:r1539_5 |
| ir.cpp:1539:8:1539:8 | Address | &:r1539_5 |
| ir.cpp:1539:8:1539:8 | Address | &:r1539_5 |
| ir.cpp:1539:8:1539:8 | Address | &:r1539_7 |
| ir.cpp:1539:8:1539:8 | Address | &:r1539_7 |
| ir.cpp:1539:8:1539:8 | Address | &:r1539_7 |
| ir.cpp:1539:8:1539:8 | Address | &:r1539_7 |
| ir.cpp:1539:8:1539:8 | Address | &:r1539_9 |
| ir.cpp:1539:8:1539:8 | Address | &:r1539_10 |
| ir.cpp:1539:8:1539:8 | Address | &:r1539_12 |
| ir.cpp:1539:8:1539:8 | Address | &:r1539_16 |
| ir.cpp:1539:8:1539:8 | Address | &:r1539_17 |
| ir.cpp:1539:8:1539:8 | Address | &:r1539_19 |
| ir.cpp:1539:8:1539:8 | Address | &:r1539_23 |
| ir.cpp:1539:8:1539:8 | Address | &:r1539_24 |
| ir.cpp:1539:8:1539:8 | Address | &:r1539_26 |
| ir.cpp:1539:8:1539:8 | ChiPartial | partial:m1539_3 |
| ir.cpp:1539:8:1539:8 | ChiPartial | partial:m1539_3 |
| ir.cpp:1539:8:1539:8 | ChiPartial | partial:m1539_14 |
| ir.cpp:1539:8:1539:8 | ChiPartial | partial:m1539_21 |
| ir.cpp:1539:8:1539:8 | ChiPartial | partial:m1539_28 |
| ir.cpp:1539:8:1539:8 | ChiTotal | total:m1539_2 |
| ir.cpp:1539:8:1539:8 | ChiTotal | total:m1539_2 |
| ir.cpp:1539:8:1539:8 | ChiTotal | total:m1539_8 |
| ir.cpp:1539:8:1539:8 | ChiTotal | total:m1539_15 |
| ir.cpp:1539:8:1539:8 | ChiTotal | total:m1539_22 |
| ir.cpp:1539:8:1539:8 | Load | m0_2 |
| ir.cpp:1539:8:1539:8 | Load | m0_2 |
| ir.cpp:1539:8:1539:8 | Load | m0_2 |
| ir.cpp:1539:8:1539:8 | Load | m1539_6 |
| ir.cpp:1539:8:1539:8 | Load | m1539_6 |
| ir.cpp:1539:8:1539:8 | Load | ~m0_4 |
| ir.cpp:1539:8:1539:8 | Load | ~m0_4 |
| ir.cpp:1539:8:1539:8 | Load | ~m0_4 |
| ir.cpp:1539:8:1539:8 | SideEffect | m1539_3 |
| ir.cpp:1539:8:1539:8 | SideEffect | m1539_3 |
| ir.cpp:1539:8:1539:8 | SideEffect | m1539_8 |
| ir.cpp:1539:8:1539:8 | SideEffect | m1539_29 |
| ir.cpp:1539:8:1539:8 | StoreValue | r1539_13 |
| ir.cpp:1539:8:1539:8 | StoreValue | r1539_20 |
| ir.cpp:1539:8:1539:8 | StoreValue | r1539_27 |
| ir.cpp:1539:8:1539:8 | Unary | m1539_6 |
| ir.cpp:1539:8:1539:8 | Unary | m1539_6 |
| ir.cpp:1539:8:1539:8 | Unary | m1539_6 |
| ir.cpp:1539:8:1539:8 | Unary | r1539_11 |
| ir.cpp:1539:8:1539:8 | Unary | r1539_18 |
| ir.cpp:1539:8:1539:8 | Unary | r1539_25 |
| ir.cpp:1567:60:1567:95 | Address | &:r1567_5 |
| ir.cpp:1567:60:1567:95 | Address | &:r1567_5 |
| ir.cpp:1567:60:1567:95 | Address | &:r1567_7 |

View File

@@ -49,6 +49,38 @@ bad_asts.cpp:
# 14| v14_5(void) = AliasedUse : ~m?
# 14| v14_6(void) = ExitFunction :
# 19| void Bad::Point::Point(Bad::Point const&)
# 19| Block 0
# 19| v19_1(void) = EnterFunction :
# 19| mu19_2(unknown) = AliasedDefinition :
# 19| mu19_3(unknown) = InitializeNonLocal :
# 19| r19_4(glval<unknown>) = VariableAddress[#this] :
# 19| mu19_5(glval<Point>) = InitializeParameter[#this] : &:r19_4
# 19| r19_6(glval<Point>) = Load[#this] : &:r19_4, ~m?
# 19| mu19_7(Point) = InitializeIndirection[#this] : &:r19_6
#-----| r0_1(glval<Point &>) = VariableAddress[(unnamed parameter 0)] :
#-----| mu0_2(Point &) = InitializeParameter[(unnamed parameter 0)] : &:r0_1
#-----| r0_3(Point &) = Load[(unnamed parameter 0)] : &:r0_1, ~m?
#-----| mu0_4(unknown) = InitializeIndirection[(unnamed parameter 0)] : &:r0_3
# 19| r19_8(glval<int>) = FieldAddress[x] : mu19_5
# 19| r19_9(glval<Point &>) = VariableAddress[(unnamed parameter 0)] :
# 19| r19_10(Point &) = Load[(unnamed parameter 0)] : &:r19_9, ~m?
# 19| r19_11(glval<int>) = FieldAddress[x] : r19_10
# 19| r19_12(int) = Load[?] : &:r19_11, ~m?
# 19| mu19_13(int) = Store[?] : &:r19_8, r19_12
# 19| r19_14(glval<int>) = FieldAddress[y] : mu19_5
# 19| r19_15(glval<Point &>) = VariableAddress[(unnamed parameter 0)] :
# 19| r19_16(Point &) = Load[(unnamed parameter 0)] : &:r19_15, ~m?
# 19| r19_17(glval<int>) = FieldAddress[y] : r19_16
# 19| r19_18(int) = Load[?] : &:r19_17, ~m?
# 19| mu19_19(int) = Store[?] : &:r19_14, r19_18
# 19| v19_20(void) = NoOp :
# 19| v19_21(void) = ReturnIndirection[#this] : &:r19_6, ~m?
#-----| v0_5(void) = ReturnIndirection[(unnamed parameter 0)] : &:r0_3, ~m?
# 19| v19_22(void) = ReturnVoid :
# 19| v19_23(void) = AliasedUse : ~m?
# 19| v19_24(void) = ExitFunction :
# 22| void Bad::Point::Point()
# 22| Block 0
# 22| v22_1(void) = EnterFunction :
@@ -8029,6 +8061,74 @@ ir.cpp:
# 1486| v1486_16(void) = AliasedUse : ~m?
# 1486| v1486_17(void) = ExitFunction :
# 1486| void StructuredBindingDataMemberStruct::StructuredBindingDataMemberStruct(StructuredBindingDataMemberStruct const&)
# 1486| Block 0
# 1486| v1486_1(void) = EnterFunction :
# 1486| mu1486_2(unknown) = AliasedDefinition :
# 1486| mu1486_3(unknown) = InitializeNonLocal :
# 1486| r1486_4(glval<unknown>) = VariableAddress[#this] :
# 1486| mu1486_5(glval<StructuredBindingDataMemberStruct>) = InitializeParameter[#this] : &:r1486_4
# 1486| r1486_6(glval<StructuredBindingDataMemberStruct>) = Load[#this] : &:r1486_4, ~m?
# 1486| mu1486_7(StructuredBindingDataMemberStruct) = InitializeIndirection[#this] : &:r1486_6
#-----| r0_1(glval<StructuredBindingDataMemberStruct &>) = VariableAddress[(unnamed parameter 0)] :
#-----| mu0_2(StructuredBindingDataMemberStruct &) = InitializeParameter[(unnamed parameter 0)] : &:r0_1
#-----| r0_3(StructuredBindingDataMemberStruct &) = Load[(unnamed parameter 0)] : &:r0_1, ~m?
#-----| mu0_4(unknown) = InitializeIndirection[(unnamed parameter 0)] : &:r0_3
# 1486| r1486_8(glval<int>) = FieldAddress[i] : mu1486_5
# 1486| r1486_9(glval<StructuredBindingDataMemberStruct &>) = VariableAddress[(unnamed parameter 0)] :
# 1486| r1486_10(StructuredBindingDataMemberStruct &) = Load[(unnamed parameter 0)] : &:r1486_9, ~m?
# 1486| r1486_11(glval<int>) = FieldAddress[i] : r1486_10
# 1486| r1486_12(int) = Load[?] : &:r1486_11, ~m?
# 1486| mu1486_13(int) = Store[?] : &:r1486_8, r1486_12
# 1486| r1486_14(glval<double>) = FieldAddress[d] : mu1486_5
# 1486| r1486_15(glval<StructuredBindingDataMemberStruct &>) = VariableAddress[(unnamed parameter 0)] :
# 1486| r1486_16(StructuredBindingDataMemberStruct &) = Load[(unnamed parameter 0)] : &:r1486_15, ~m?
# 1486| r1486_17(glval<double>) = FieldAddress[d] : r1486_16
# 1486| r1486_18(double) = Load[?] : &:r1486_17, ~m?
# 1486| mu1486_19(double) = Store[?] : &:r1486_14, r1486_18
# 1486| r1486_20(glval<unsigned int>) = FieldAddress[b] : mu1486_5
# 1486| r1486_21(glval<StructuredBindingDataMemberStruct &>) = VariableAddress[(unnamed parameter 0)] :
# 1486| r1486_22(StructuredBindingDataMemberStruct &) = Load[(unnamed parameter 0)] : &:r1486_21, ~m?
# 1486| r1486_23(glval<unsigned int>) = FieldAddress[b] : r1486_22
# 1486| r1486_24(unsigned int) = Load[?] : &:r1486_23, ~m?
# 1486| mu1486_25(unsigned int) = Store[?] : &:r1486_20, r1486_24
# 1486| r1486_26(glval<int &>) = FieldAddress[r] : mu1486_5
# 1486| r1486_27(glval<StructuredBindingDataMemberStruct &>) = VariableAddress[(unnamed parameter 0)] :
# 1486| r1486_28(StructuredBindingDataMemberStruct &) = Load[(unnamed parameter 0)] : &:r1486_27, ~m?
# 1486| r1486_29(glval<int &>) = FieldAddress[r] : r1486_28
# 1486| r1486_30(int &) = Load[?] : &:r1486_29, ~m?
# 1486| mu1486_31(int &) = Store[?] : &:r1486_26, r1486_30
# 1486| r1486_32(glval<int *>) = FieldAddress[p] : mu1486_5
# 1486| r1486_33(glval<StructuredBindingDataMemberStruct &>) = VariableAddress[(unnamed parameter 0)] :
# 1486| r1486_34(StructuredBindingDataMemberStruct &) = Load[(unnamed parameter 0)] : &:r1486_33, ~m?
# 1486| r1486_35(glval<int *>) = FieldAddress[p] : r1486_34
# 1486| r1486_36(int *) = Load[?] : &:r1486_35, ~m?
# 1486| mu1486_37(int *) = Store[?] : &:r1486_32, r1486_36
# 1486| r1486_38(glval<int[2]>) = FieldAddress[xs] : mu1486_5
# 1486| r1486_39(glval<StructuredBindingDataMemberStruct &>) = VariableAddress[(unnamed parameter 0)] :
# 1486| r1486_40(StructuredBindingDataMemberStruct &) = Load[(unnamed parameter 0)] : &:r1486_39, ~m?
# 1486| r1486_41(glval<int[2]>) = FieldAddress[xs] : r1486_40
# 1486| r1486_42(int[2]) = Load[?] : &:r1486_41, ~m?
# 1486| mu1486_43(int[2]) = Store[?] : &:r1486_38, r1486_42
# 1486| r1486_44(glval<int &>) = FieldAddress[r_alt] : mu1486_5
# 1486| r1486_45(glval<StructuredBindingDataMemberStruct &>) = VariableAddress[(unnamed parameter 0)] :
# 1486| r1486_46(StructuredBindingDataMemberStruct &) = Load[(unnamed parameter 0)] : &:r1486_45, ~m?
# 1486| r1486_47(glval<int &>) = FieldAddress[r_alt] : r1486_46
# 1486| r1486_48(int &) = Load[?] : &:r1486_47, ~m?
# 1486| mu1486_49(int &) = Store[?] : &:r1486_44, r1486_48
# 1486| r1486_50(glval<StructuredBindingDataMemberMemberStruct>) = FieldAddress[m] : mu1486_5
# 1486| r1486_51(glval<StructuredBindingDataMemberStruct &>) = VariableAddress[(unnamed parameter 0)] :
# 1486| r1486_52(StructuredBindingDataMemberStruct &) = Load[(unnamed parameter 0)] : &:r1486_51, ~m?
# 1486| r1486_53(glval<StructuredBindingDataMemberMemberStruct>) = FieldAddress[m] : r1486_52
# 1486| r1486_54(StructuredBindingDataMemberMemberStruct) = Load[?] : &:r1486_53, ~m?
# 1486| mu1486_55(StructuredBindingDataMemberMemberStruct) = Store[?] : &:r1486_50, r1486_54
# 1486| v1486_56(void) = NoOp :
# 1486| v1486_57(void) = ReturnIndirection[#this] : &:r1486_6, ~m?
#-----| v0_5(void) = ReturnIndirection[(unnamed parameter 0)] : &:r0_3, ~m?
# 1486| v1486_58(void) = ReturnVoid :
# 1486| v1486_59(void) = AliasedUse : ~m?
# 1486| v1486_60(void) = ExitFunction :
# 1499| void data_member_structured_binding()
# 1499| Block 0
# 1499| v1499_1(void) = EnterFunction :
@@ -8209,6 +8309,44 @@ ir.cpp:
# 1539| v1539_11(void) = AliasedUse : ~m?
# 1539| v1539_12(void) = ExitFunction :
# 1539| void StructuredBindingTupleRefGet::StructuredBindingTupleRefGet(StructuredBindingTupleRefGet const&)
# 1539| Block 0
# 1539| v1539_1(void) = EnterFunction :
# 1539| mu1539_2(unknown) = AliasedDefinition :
# 1539| mu1539_3(unknown) = InitializeNonLocal :
# 1539| r1539_4(glval<unknown>) = VariableAddress[#this] :
# 1539| mu1539_5(glval<StructuredBindingTupleRefGet>) = InitializeParameter[#this] : &:r1539_4
# 1539| r1539_6(glval<StructuredBindingTupleRefGet>) = Load[#this] : &:r1539_4, ~m?
# 1539| mu1539_7(StructuredBindingTupleRefGet) = InitializeIndirection[#this] : &:r1539_6
#-----| r0_1(glval<StructuredBindingTupleRefGet &>) = VariableAddress[(unnamed parameter 0)] :
#-----| mu0_2(StructuredBindingTupleRefGet &) = InitializeParameter[(unnamed parameter 0)] : &:r0_1
#-----| r0_3(StructuredBindingTupleRefGet &) = Load[(unnamed parameter 0)] : &:r0_1, ~m?
#-----| mu0_4(unknown) = InitializeIndirection[(unnamed parameter 0)] : &:r0_3
# 1539| r1539_8(glval<int>) = FieldAddress[i] : mu1539_5
# 1539| r1539_9(glval<StructuredBindingTupleRefGet &>) = VariableAddress[(unnamed parameter 0)] :
# 1539| r1539_10(StructuredBindingTupleRefGet &) = Load[(unnamed parameter 0)] : &:r1539_9, ~m?
# 1539| r1539_11(glval<int>) = FieldAddress[i] : r1539_10
# 1539| r1539_12(int) = Load[?] : &:r1539_11, ~m?
# 1539| mu1539_13(int) = Store[?] : &:r1539_8, r1539_12
# 1539| r1539_14(glval<double>) = FieldAddress[d] : mu1539_5
# 1539| r1539_15(glval<StructuredBindingTupleRefGet &>) = VariableAddress[(unnamed parameter 0)] :
# 1539| r1539_16(StructuredBindingTupleRefGet &) = Load[(unnamed parameter 0)] : &:r1539_15, ~m?
# 1539| r1539_17(glval<double>) = FieldAddress[d] : r1539_16
# 1539| r1539_18(double) = Load[?] : &:r1539_17, ~m?
# 1539| mu1539_19(double) = Store[?] : &:r1539_14, r1539_18
# 1539| r1539_20(glval<int &>) = FieldAddress[r] : mu1539_5
# 1539| r1539_21(glval<StructuredBindingTupleRefGet &>) = VariableAddress[(unnamed parameter 0)] :
# 1539| r1539_22(StructuredBindingTupleRefGet &) = Load[(unnamed parameter 0)] : &:r1539_21, ~m?
# 1539| r1539_23(glval<int &>) = FieldAddress[r] : r1539_22
# 1539| r1539_24(int &) = Load[?] : &:r1539_23, ~m?
# 1539| mu1539_25(int &) = Store[?] : &:r1539_20, r1539_24
# 1539| v1539_26(void) = NoOp :
# 1539| v1539_27(void) = ReturnIndirection[#this] : &:r1539_6, ~m?
#-----| v0_5(void) = ReturnIndirection[(unnamed parameter 0)] : &:r0_3, ~m?
# 1539| v1539_28(void) = ReturnVoid :
# 1539| v1539_29(void) = AliasedUse : ~m?
# 1539| v1539_30(void) = ExitFunction :
# 1567| std::tuple_element<int 0, StructuredBindingTupleRefGet>::type& StructuredBindingTupleRefGet::get<int 0>()
# 1567| Block 0
# 1567| v1567_1(void) = EnterFunction :

View File

@@ -125,8 +125,8 @@
| captures.cpp:22:19:22:19 | (unnamed constructor) |
| captures.cpp:22:19:22:19 | (unnamed constructor) |
| captures.cpp:22:19:22:19 | (unnamed constructor) |
| captures.cpp:22:19:22:19 | Unknown literal |
| captures.cpp:22:19:22:19 | Unknown literal |
| captures.cpp:22:19:22:19 | (unnamed parameter 0) |
| captures.cpp:22:19:22:19 | (unnamed parameter 0) |
| captures.cpp:22:19:22:19 | constructor init of field x |
| captures.cpp:22:19:22:19 | constructor init of field y |
| captures.cpp:22:19:22:19 | declaration of (unnamed constructor) |
@@ -135,6 +135,8 @@
| captures.cpp:22:19:22:19 | definition of operator= |
| captures.cpp:22:19:22:19 | operator= |
| captures.cpp:22:19:22:19 | return ... |
| captures.cpp:22:19:22:19 | x |
| captures.cpp:22:19:22:19 | y |
| captures.cpp:22:19:22:19 | { ... } |
| captures.cpp:22:23:22:23 | definition of x |
| captures.cpp:22:23:22:23 | x |
@@ -185,12 +187,13 @@
| end_pos.cpp:9:15:9:15 | (unnamed constructor) |
| end_pos.cpp:9:15:9:15 | (unnamed constructor) |
| end_pos.cpp:9:15:9:15 | (unnamed constructor) |
| end_pos.cpp:9:15:9:15 | Unknown literal |
| end_pos.cpp:9:15:9:15 | (unnamed parameter 0) |
| end_pos.cpp:9:15:9:15 | constructor init of field ii |
| end_pos.cpp:9:15:9:15 | declaration of (unnamed constructor) |
| end_pos.cpp:9:15:9:15 | definition of (unnamed constructor) |
| end_pos.cpp:9:15:9:15 | definition of (unnamed constructor) |
| end_pos.cpp:9:15:9:15 | definition of operator= |
| end_pos.cpp:9:15:9:15 | ii |
| end_pos.cpp:9:15:9:15 | operator= |
| end_pos.cpp:9:15:9:15 | return ... |
| end_pos.cpp:9:15:9:15 | { ... } |

View File

@@ -1498,6 +1498,8 @@ postWithInFlow
| bad_asts.cpp:15:10:15:12 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
| bad_asts.cpp:16:5:16:5 | s [post update] | PostUpdateNode should not be the target of local flow. |
| bad_asts.cpp:16:5:16:5 | s [post update] | PostUpdateNode should not be the target of local flow. |
| bad_asts.cpp:19:10:19:10 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
| bad_asts.cpp:19:10:19:10 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
| bad_asts.cpp:27:11:27:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| break_labels.c:3:9:3:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| break_labels.c:5:9:5:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
@@ -1626,11 +1628,15 @@ postWithInFlow
| cpp11.cpp:60:15:60:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| cpp11.cpp:65:10:65:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| cpp11.cpp:65:19:65:45 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
| cpp11.cpp:65:20:65:20 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
| cpp11.cpp:65:35:65:43 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| cpp11.cpp:77:19:77:21 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
| cpp11.cpp:77:19:77:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| cpp11.cpp:82:11:82:14 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
| cpp11.cpp:82:11:82:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| cpp11.cpp:82:17:82:17 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
| cpp11.cpp:82:17:82:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
| cpp11.cpp:82:17:82:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
| cpp11.cpp:82:17:82:55 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
| cpp11.cpp:82:17:82:55 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
| cpp11.cpp:82:17:82:55 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |

View File

@@ -0,0 +1,8 @@
edges
| tests.c:57:21:57:28 | password | tests.c:70:70:70:77 | array to pointer conversion |
nodes
| tests.c:57:21:57:28 | password | semmle.label | password |
| tests.c:70:70:70:77 | array to pointer conversion | semmle.label | array to pointer conversion |
subpaths
#select
| tests.c:70:70:70:77 | array to pointer conversion | tests.c:57:21:57:28 | password | tests.c:70:70:70:77 | array to pointer conversion | This operation potentially exposes sensitive system data from $@. | tests.c:57:21:57:28 | password | password |

View File

@@ -0,0 +1 @@
Security/CWE/CWE-497/PotentiallyExposedSystemData.ql

View File

@@ -67,6 +67,6 @@ void CWE535_Info_Exposure_Shell_Error__w32_char_01_bad()
printLine("Unable to login.");
}
/* FLAW: Write sensitive data to stderr */
fprintf(stderr, "User attempted access with password: %s\n", password); // [NOT DETECTED]
fprintf(stderr, "User attempted access with password: %s\n", password);
}
}

View File

@@ -2,15 +2,26 @@ edges
| tests2.cpp:63:13:63:18 | call to getenv | tests2.cpp:63:13:63:26 | (const char *)... |
| tests2.cpp:64:13:64:18 | call to getenv | tests2.cpp:64:13:64:26 | (const char *)... |
| tests2.cpp:65:13:65:18 | call to getenv | tests2.cpp:65:13:65:30 | (const char *)... |
| tests2.cpp:76:18:76:38 | call to mysql_get_client_info | tests2.cpp:79:14:79:19 | (const char *)... |
| tests2.cpp:78:14:78:34 | call to mysql_get_client_info | tests2.cpp:78:14:78:34 | call to mysql_get_client_info |
| tests2.cpp:78:14:78:34 | call to mysql_get_client_info | tests2.cpp:78:14:78:34 | call to mysql_get_client_info |
| tests2.cpp:89:42:89:45 | str1 | tests2.cpp:91:14:91:17 | str1 |
| tests2.cpp:99:8:99:15 | call to getpwuid | tests2.cpp:100:14:100:15 | pw |
| tests2.cpp:107:3:107:4 | c1 [post update] [ptr] | tests2.cpp:109:14:109:15 | c1 [read] [ptr] |
| tests2.cpp:107:6:107:8 | ptr [post update] | tests2.cpp:107:3:107:4 | c1 [post update] [ptr] |
| tests2.cpp:107:12:107:17 | call to getenv | tests2.cpp:107:6:107:8 | ptr [post update] |
| tests2.cpp:109:14:109:15 | c1 [read] [ptr] | tests2.cpp:109:14:109:19 | (const char *)... |
| tests2.cpp:66:13:66:18 | call to getenv | tests2.cpp:66:13:66:34 | (const char *)... |
| tests2.cpp:78:18:78:38 | call to mysql_get_client_info | tests2.cpp:81:14:81:19 | (const char *)... |
| tests2.cpp:80:14:80:34 | call to mysql_get_client_info | tests2.cpp:80:14:80:34 | call to mysql_get_client_info |
| tests2.cpp:80:14:80:34 | call to mysql_get_client_info | tests2.cpp:80:14:80:34 | call to mysql_get_client_info |
| tests2.cpp:91:42:91:45 | str1 | tests2.cpp:93:14:93:17 | str1 |
| tests2.cpp:101:8:101:15 | call to getpwuid | tests2.cpp:102:14:102:15 | pw |
| tests2.cpp:109:3:109:4 | c1 [post update] [ptr] | tests2.cpp:111:14:111:15 | c1 [read] [ptr] |
| tests2.cpp:109:6:109:8 | ptr [post update] | tests2.cpp:109:3:109:4 | c1 [post update] [ptr] |
| tests2.cpp:109:12:109:17 | call to getenv | tests2.cpp:109:6:109:8 | ptr [post update] |
| tests2.cpp:111:14:111:15 | c1 [read] [ptr] | tests2.cpp:111:14:111:19 | (const char *)... |
| tests_sockets.cpp:26:15:26:20 | call to getenv | tests_sockets.cpp:39:19:39:22 | (const void *)... |
| tests_sockets.cpp:26:15:26:20 | call to getenv | tests_sockets.cpp:39:19:39:22 | path |
| tests_sockets.cpp:26:15:26:20 | call to getenv | tests_sockets.cpp:43:20:43:23 | (const void *)... |
| tests_sockets.cpp:26:15:26:20 | call to getenv | tests_sockets.cpp:43:20:43:23 | path |
| tests_sockets.cpp:63:15:63:20 | call to getenv | tests_sockets.cpp:76:19:76:22 | (const void *)... |
| tests_sockets.cpp:63:15:63:20 | call to getenv | tests_sockets.cpp:76:19:76:22 | path |
| tests_sockets.cpp:63:15:63:20 | call to getenv | tests_sockets.cpp:80:20:80:23 | (const void *)... |
| tests_sockets.cpp:63:15:63:20 | call to getenv | tests_sockets.cpp:80:20:80:23 | path |
| tests_sysconf.cpp:36:21:36:27 | confstr output argument | tests_sysconf.cpp:39:19:39:25 | (const void *)... |
| tests_sysconf.cpp:36:21:36:27 | confstr output argument | tests_sysconf.cpp:39:19:39:25 | pathbuf |
nodes
| tests2.cpp:63:13:63:18 | call to getenv | semmle.label | call to getenv |
| tests2.cpp:63:13:63:18 | call to getenv | semmle.label | call to getenv |
@@ -21,27 +32,49 @@ nodes
| tests2.cpp:65:13:65:18 | call to getenv | semmle.label | call to getenv |
| tests2.cpp:65:13:65:18 | call to getenv | semmle.label | call to getenv |
| tests2.cpp:65:13:65:30 | (const char *)... | semmle.label | (const char *)... |
| tests2.cpp:76:18:76:38 | call to mysql_get_client_info | semmle.label | call to mysql_get_client_info |
| tests2.cpp:78:14:78:34 | call to mysql_get_client_info | semmle.label | call to mysql_get_client_info |
| tests2.cpp:78:14:78:34 | call to mysql_get_client_info | semmle.label | call to mysql_get_client_info |
| tests2.cpp:79:14:79:19 | (const char *)... | semmle.label | (const char *)... |
| tests2.cpp:89:42:89:45 | str1 | semmle.label | str1 |
| tests2.cpp:91:14:91:17 | str1 | semmle.label | str1 |
| tests2.cpp:99:8:99:15 | call to getpwuid | semmle.label | call to getpwuid |
| tests2.cpp:100:14:100:15 | pw | semmle.label | pw |
| tests2.cpp:107:3:107:4 | c1 [post update] [ptr] | semmle.label | c1 [post update] [ptr] |
| tests2.cpp:107:6:107:8 | ptr [post update] | semmle.label | ptr [post update] |
| tests2.cpp:107:12:107:17 | call to getenv | semmle.label | call to getenv |
| tests2.cpp:109:14:109:15 | c1 [read] [ptr] | semmle.label | c1 [read] [ptr] |
| tests2.cpp:109:14:109:19 | (const char *)... | semmle.label | (const char *)... |
| tests2.cpp:66:13:66:18 | call to getenv | semmle.label | call to getenv |
| tests2.cpp:66:13:66:18 | call to getenv | semmle.label | call to getenv |
| tests2.cpp:66:13:66:34 | (const char *)... | semmle.label | (const char *)... |
| tests2.cpp:78:18:78:38 | call to mysql_get_client_info | semmle.label | call to mysql_get_client_info |
| tests2.cpp:80:14:80:34 | call to mysql_get_client_info | semmle.label | call to mysql_get_client_info |
| tests2.cpp:80:14:80:34 | call to mysql_get_client_info | semmle.label | call to mysql_get_client_info |
| tests2.cpp:81:14:81:19 | (const char *)... | semmle.label | (const char *)... |
| tests2.cpp:91:42:91:45 | str1 | semmle.label | str1 |
| tests2.cpp:93:14:93:17 | str1 | semmle.label | str1 |
| tests2.cpp:101:8:101:15 | call to getpwuid | semmle.label | call to getpwuid |
| tests2.cpp:102:14:102:15 | pw | semmle.label | pw |
| tests2.cpp:109:3:109:4 | c1 [post update] [ptr] | semmle.label | c1 [post update] [ptr] |
| tests2.cpp:109:6:109:8 | ptr [post update] | semmle.label | ptr [post update] |
| tests2.cpp:109:12:109:17 | call to getenv | semmle.label | call to getenv |
| tests2.cpp:111:14:111:15 | c1 [read] [ptr] | semmle.label | c1 [read] [ptr] |
| tests2.cpp:111:14:111:19 | (const char *)... | semmle.label | (const char *)... |
| tests_sockets.cpp:26:15:26:20 | call to getenv | semmle.label | call to getenv |
| tests_sockets.cpp:39:19:39:22 | (const void *)... | semmle.label | (const void *)... |
| tests_sockets.cpp:39:19:39:22 | path | semmle.label | path |
| tests_sockets.cpp:43:20:43:23 | (const void *)... | semmle.label | (const void *)... |
| tests_sockets.cpp:43:20:43:23 | path | semmle.label | path |
| tests_sockets.cpp:63:15:63:20 | call to getenv | semmle.label | call to getenv |
| tests_sockets.cpp:76:19:76:22 | (const void *)... | semmle.label | (const void *)... |
| tests_sockets.cpp:76:19:76:22 | path | semmle.label | path |
| tests_sockets.cpp:80:20:80:23 | (const void *)... | semmle.label | (const void *)... |
| tests_sockets.cpp:80:20:80:23 | path | semmle.label | path |
| tests_sysconf.cpp:36:21:36:27 | confstr output argument | semmle.label | confstr output argument |
| tests_sysconf.cpp:39:19:39:25 | (const void *)... | semmle.label | (const void *)... |
| tests_sysconf.cpp:39:19:39:25 | pathbuf | semmle.label | pathbuf |
subpaths
#select
| tests2.cpp:63:13:63:18 | call to getenv | tests2.cpp:63:13:63:18 | call to getenv | tests2.cpp:63:13:63:18 | call to getenv | This operation exposes system data from $@. | tests2.cpp:63:13:63:18 | call to getenv | call to getenv |
| tests2.cpp:64:13:64:18 | call to getenv | tests2.cpp:64:13:64:18 | call to getenv | tests2.cpp:64:13:64:18 | call to getenv | This operation exposes system data from $@. | tests2.cpp:64:13:64:18 | call to getenv | call to getenv |
| tests2.cpp:65:13:65:18 | call to getenv | tests2.cpp:65:13:65:18 | call to getenv | tests2.cpp:65:13:65:18 | call to getenv | This operation exposes system data from $@. | tests2.cpp:65:13:65:18 | call to getenv | call to getenv |
| tests2.cpp:78:14:78:34 | call to mysql_get_client_info | tests2.cpp:78:14:78:34 | call to mysql_get_client_info | tests2.cpp:78:14:78:34 | call to mysql_get_client_info | This operation exposes system data from $@. | tests2.cpp:78:14:78:34 | call to mysql_get_client_info | call to mysql_get_client_info |
| tests2.cpp:78:14:78:34 | call to mysql_get_client_info | tests2.cpp:78:14:78:34 | call to mysql_get_client_info | tests2.cpp:78:14:78:34 | call to mysql_get_client_info | This operation exposes system data from $@. | tests2.cpp:78:14:78:34 | call to mysql_get_client_info | call to mysql_get_client_info |
| tests2.cpp:79:14:79:19 | (const char *)... | tests2.cpp:76:18:76:38 | call to mysql_get_client_info | tests2.cpp:79:14:79:19 | (const char *)... | This operation exposes system data from $@. | tests2.cpp:76:18:76:38 | call to mysql_get_client_info | call to mysql_get_client_info |
| tests2.cpp:91:14:91:17 | str1 | tests2.cpp:89:42:89:45 | str1 | tests2.cpp:91:14:91:17 | str1 | This operation exposes system data from $@. | tests2.cpp:89:42:89:45 | str1 | str1 |
| tests2.cpp:100:14:100:15 | pw | tests2.cpp:99:8:99:15 | call to getpwuid | tests2.cpp:100:14:100:15 | pw | This operation exposes system data from $@. | tests2.cpp:99:8:99:15 | call to getpwuid | call to getpwuid |
| tests2.cpp:109:14:109:19 | (const char *)... | tests2.cpp:107:12:107:17 | call to getenv | tests2.cpp:109:14:109:19 | (const char *)... | This operation exposes system data from $@. | tests2.cpp:107:12:107:17 | call to getenv | call to getenv |
| tests2.cpp:66:13:66:18 | call to getenv | tests2.cpp:66:13:66:18 | call to getenv | tests2.cpp:66:13:66:18 | call to getenv | This operation exposes system data from $@. | tests2.cpp:66:13:66:18 | call to getenv | call to getenv |
| tests2.cpp:80:14:80:34 | call to mysql_get_client_info | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | This operation exposes system data from $@. | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | call to mysql_get_client_info |
| tests2.cpp:80:14:80:34 | call to mysql_get_client_info | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | This operation exposes system data from $@. | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | call to mysql_get_client_info |
| tests2.cpp:81:14:81:19 | (const char *)... | tests2.cpp:78:18:78:38 | call to mysql_get_client_info | tests2.cpp:81:14:81:19 | (const char *)... | This operation exposes system data from $@. | tests2.cpp:78:18:78:38 | call to mysql_get_client_info | call to mysql_get_client_info |
| tests2.cpp:93:14:93:17 | str1 | tests2.cpp:91:42:91:45 | str1 | tests2.cpp:93:14:93:17 | str1 | This operation exposes system data from $@. | tests2.cpp:91:42:91:45 | str1 | str1 |
| tests2.cpp:102:14:102:15 | pw | tests2.cpp:101:8:101:15 | call to getpwuid | tests2.cpp:102:14:102:15 | pw | This operation exposes system data from $@. | tests2.cpp:101:8:101:15 | call to getpwuid | call to getpwuid |
| tests2.cpp:111:14:111:19 | (const char *)... | tests2.cpp:109:12:109:17 | call to getenv | tests2.cpp:111:14:111:19 | (const char *)... | This operation exposes system data from $@. | tests2.cpp:109:12:109:17 | call to getenv | call to getenv |
| tests_sockets.cpp:39:19:39:22 | path | tests_sockets.cpp:26:15:26:20 | call to getenv | tests_sockets.cpp:39:19:39:22 | path | This operation exposes system data from $@. | tests_sockets.cpp:26:15:26:20 | call to getenv | call to getenv |
| tests_sockets.cpp:43:20:43:23 | path | tests_sockets.cpp:26:15:26:20 | call to getenv | tests_sockets.cpp:43:20:43:23 | path | This operation exposes system data from $@. | tests_sockets.cpp:26:15:26:20 | call to getenv | call to getenv |
| tests_sockets.cpp:76:19:76:22 | path | tests_sockets.cpp:63:15:63:20 | call to getenv | tests_sockets.cpp:76:19:76:22 | path | This operation exposes system data from $@. | tests_sockets.cpp:63:15:63:20 | call to getenv | call to getenv |
| tests_sockets.cpp:80:20:80:23 | path | tests_sockets.cpp:63:15:63:20 | call to getenv | tests_sockets.cpp:80:20:80:23 | path | This operation exposes system data from $@. | tests_sockets.cpp:63:15:63:20 | call to getenv | call to getenv |
| tests_sysconf.cpp:39:19:39:25 | pathbuf | tests_sysconf.cpp:36:21:36:27 | confstr output argument | tests_sysconf.cpp:39:19:39:25 | pathbuf | This operation exposes system data from $@. | tests_sysconf.cpp:36:21:36:27 | confstr output argument | confstr output argument |

View File

@@ -0,0 +1,109 @@
edges
| tests.cpp:48:15:48:20 | call to getenv | tests.cpp:48:15:48:36 | (const char *)... |
| tests.cpp:49:15:49:20 | call to getenv | tests.cpp:49:15:49:36 | (const char *)... |
| tests.cpp:50:15:50:20 | call to getenv | tests.cpp:50:15:50:36 | (const char *)... |
| tests.cpp:57:18:57:23 | call to getenv | tests.cpp:57:18:57:39 | (const char_type *)... |
| tests.cpp:58:41:58:46 | call to getenv | tests.cpp:58:41:58:62 | (const char_type *)... |
| tests.cpp:59:43:59:48 | call to getenv | tests.cpp:59:43:59:64 | (const char *)... |
| tests.cpp:86:29:86:31 | *msg | tests.cpp:88:15:88:17 | msg |
| tests.cpp:86:29:86:31 | msg | tests.cpp:88:15:88:17 | msg |
| tests.cpp:97:13:97:18 | call to getenv | tests.cpp:97:13:97:34 | (const char *)... |
| tests.cpp:97:13:97:18 | call to getenv | tests.cpp:97:13:97:34 | call to getenv |
| tests.cpp:97:13:97:18 | call to getenv | tests.cpp:97:13:97:34 | call to getenv indirection |
| tests.cpp:97:13:97:34 | call to getenv | tests.cpp:86:29:86:31 | msg |
| tests.cpp:97:13:97:34 | call to getenv indirection | tests.cpp:86:29:86:31 | *msg |
| tests.cpp:107:30:107:32 | *msg | tests.cpp:111:15:111:17 | tmp |
| tests.cpp:107:30:107:32 | msg | tests.cpp:111:15:111:17 | tmp |
| tests.cpp:114:30:114:32 | *msg | tests.cpp:119:7:119:12 | (const char *)... |
| tests.cpp:114:30:114:32 | msg | tests.cpp:119:7:119:12 | (const char *)... |
| tests.cpp:122:30:122:32 | *msg | tests.cpp:124:15:124:17 | msg |
| tests.cpp:122:30:122:32 | msg | tests.cpp:124:15:124:17 | msg |
| tests.cpp:131:14:131:19 | call to getenv | tests.cpp:131:14:131:35 | call to getenv |
| tests.cpp:131:14:131:19 | call to getenv | tests.cpp:131:14:131:35 | call to getenv indirection |
| tests.cpp:131:14:131:35 | call to getenv | tests.cpp:107:30:107:32 | msg |
| tests.cpp:131:14:131:35 | call to getenv indirection | tests.cpp:107:30:107:32 | *msg |
| tests.cpp:132:14:132:19 | call to getenv | tests.cpp:132:14:132:35 | call to getenv |
| tests.cpp:132:14:132:19 | call to getenv | tests.cpp:132:14:132:35 | call to getenv indirection |
| tests.cpp:132:14:132:35 | call to getenv | tests.cpp:114:30:114:32 | msg |
| tests.cpp:132:14:132:35 | call to getenv indirection | tests.cpp:114:30:114:32 | *msg |
| tests.cpp:133:14:133:19 | call to getenv | tests.cpp:133:14:133:35 | (const char *)... |
| tests.cpp:133:14:133:19 | call to getenv | tests.cpp:133:14:133:35 | call to getenv |
| tests.cpp:133:14:133:19 | call to getenv | tests.cpp:133:14:133:35 | call to getenv indirection |
| tests.cpp:133:14:133:35 | call to getenv | tests.cpp:122:30:122:32 | msg |
| tests.cpp:133:14:133:35 | call to getenv indirection | tests.cpp:122:30:122:32 | *msg |
| tests_passwd.cpp:16:8:16:15 | call to getpwnam | tests_passwd.cpp:18:29:18:31 | pwd |
| tests_passwd.cpp:16:8:16:15 | call to getpwnam | tests_passwd.cpp:19:26:19:28 | pwd |
nodes
| tests.cpp:48:15:48:20 | call to getenv | semmle.label | call to getenv |
| tests.cpp:48:15:48:20 | call to getenv | semmle.label | call to getenv |
| tests.cpp:48:15:48:36 | (const char *)... | semmle.label | (const char *)... |
| tests.cpp:49:15:49:20 | call to getenv | semmle.label | call to getenv |
| tests.cpp:49:15:49:20 | call to getenv | semmle.label | call to getenv |
| tests.cpp:49:15:49:36 | (const char *)... | semmle.label | (const char *)... |
| tests.cpp:50:15:50:20 | call to getenv | semmle.label | call to getenv |
| tests.cpp:50:15:50:20 | call to getenv | semmle.label | call to getenv |
| tests.cpp:50:15:50:36 | (const char *)... | semmle.label | (const char *)... |
| tests.cpp:57:18:57:23 | call to getenv | semmle.label | call to getenv |
| tests.cpp:57:18:57:23 | call to getenv | semmle.label | call to getenv |
| tests.cpp:57:18:57:39 | (const char_type *)... | semmle.label | (const char_type *)... |
| tests.cpp:58:41:58:46 | call to getenv | semmle.label | call to getenv |
| tests.cpp:58:41:58:46 | call to getenv | semmle.label | call to getenv |
| tests.cpp:58:41:58:62 | (const char_type *)... | semmle.label | (const char_type *)... |
| tests.cpp:59:43:59:48 | call to getenv | semmle.label | call to getenv |
| tests.cpp:59:43:59:48 | call to getenv | semmle.label | call to getenv |
| tests.cpp:59:43:59:64 | (const char *)... | semmle.label | (const char *)... |
| tests.cpp:86:29:86:31 | *msg | semmle.label | *msg |
| tests.cpp:86:29:86:31 | msg | semmle.label | msg |
| tests.cpp:88:15:88:17 | msg | semmle.label | msg |
| tests.cpp:97:13:97:18 | call to getenv | semmle.label | call to getenv |
| tests.cpp:97:13:97:18 | call to getenv | semmle.label | call to getenv |
| tests.cpp:97:13:97:34 | (const char *)... | semmle.label | (const char *)... |
| tests.cpp:97:13:97:34 | call to getenv | semmle.label | call to getenv |
| tests.cpp:97:13:97:34 | call to getenv indirection | semmle.label | call to getenv indirection |
| tests.cpp:107:30:107:32 | *msg | semmle.label | *msg |
| tests.cpp:107:30:107:32 | msg | semmle.label | msg |
| tests.cpp:111:15:111:17 | tmp | semmle.label | tmp |
| tests.cpp:114:30:114:32 | *msg | semmle.label | *msg |
| tests.cpp:114:30:114:32 | msg | semmle.label | msg |
| tests.cpp:119:7:119:12 | (const char *)... | semmle.label | (const char *)... |
| tests.cpp:122:30:122:32 | *msg | semmle.label | *msg |
| tests.cpp:122:30:122:32 | msg | semmle.label | msg |
| tests.cpp:124:15:124:17 | msg | semmle.label | msg |
| tests.cpp:131:14:131:19 | call to getenv | semmle.label | call to getenv |
| tests.cpp:131:14:131:35 | call to getenv | semmle.label | call to getenv |
| tests.cpp:131:14:131:35 | call to getenv indirection | semmle.label | call to getenv indirection |
| tests.cpp:132:14:132:19 | call to getenv | semmle.label | call to getenv |
| tests.cpp:132:14:132:35 | call to getenv | semmle.label | call to getenv |
| tests.cpp:132:14:132:35 | call to getenv indirection | semmle.label | call to getenv indirection |
| tests.cpp:133:14:133:19 | call to getenv | semmle.label | call to getenv |
| tests.cpp:133:14:133:19 | call to getenv | semmle.label | call to getenv |
| tests.cpp:133:14:133:35 | (const char *)... | semmle.label | (const char *)... |
| tests.cpp:133:14:133:35 | call to getenv | semmle.label | call to getenv |
| tests.cpp:133:14:133:35 | call to getenv indirection | semmle.label | call to getenv indirection |
| tests_passwd.cpp:16:8:16:15 | call to getpwnam | semmle.label | call to getpwnam |
| tests_passwd.cpp:18:29:18:31 | pwd | semmle.label | pwd |
| tests_passwd.cpp:19:26:19:28 | pwd | semmle.label | pwd |
subpaths
#select
| tests.cpp:48:15:48:20 | call to getenv | tests.cpp:48:15:48:20 | call to getenv | tests.cpp:48:15:48:20 | call to getenv | This operation potentially exposes sensitive system data from $@. | tests.cpp:48:15:48:20 | call to getenv | call to getenv |
| tests.cpp:48:15:48:36 | (const char *)... | tests.cpp:48:15:48:20 | call to getenv | tests.cpp:48:15:48:36 | (const char *)... | This operation potentially exposes sensitive system data from $@. | tests.cpp:48:15:48:20 | call to getenv | call to getenv |
| tests.cpp:49:15:49:20 | call to getenv | tests.cpp:49:15:49:20 | call to getenv | tests.cpp:49:15:49:20 | call to getenv | This operation potentially exposes sensitive system data from $@. | tests.cpp:49:15:49:20 | call to getenv | call to getenv |
| tests.cpp:49:15:49:36 | (const char *)... | tests.cpp:49:15:49:20 | call to getenv | tests.cpp:49:15:49:36 | (const char *)... | This operation potentially exposes sensitive system data from $@. | tests.cpp:49:15:49:20 | call to getenv | call to getenv |
| tests.cpp:50:15:50:20 | call to getenv | tests.cpp:50:15:50:20 | call to getenv | tests.cpp:50:15:50:20 | call to getenv | This operation potentially exposes sensitive system data from $@. | tests.cpp:50:15:50:20 | call to getenv | call to getenv |
| tests.cpp:50:15:50:36 | (const char *)... | tests.cpp:50:15:50:20 | call to getenv | tests.cpp:50:15:50:36 | (const char *)... | This operation potentially exposes sensitive system data from $@. | tests.cpp:50:15:50:20 | call to getenv | call to getenv |
| tests.cpp:57:18:57:23 | call to getenv | tests.cpp:57:18:57:23 | call to getenv | tests.cpp:57:18:57:23 | call to getenv | This operation potentially exposes sensitive system data from $@. | tests.cpp:57:18:57:23 | call to getenv | call to getenv |
| tests.cpp:57:18:57:39 | (const char_type *)... | tests.cpp:57:18:57:23 | call to getenv | tests.cpp:57:18:57:39 | (const char_type *)... | This operation potentially exposes sensitive system data from $@. | tests.cpp:57:18:57:23 | call to getenv | call to getenv |
| tests.cpp:58:41:58:46 | call to getenv | tests.cpp:58:41:58:46 | call to getenv | tests.cpp:58:41:58:46 | call to getenv | This operation potentially exposes sensitive system data from $@. | tests.cpp:58:41:58:46 | call to getenv | call to getenv |
| tests.cpp:58:41:58:62 | (const char_type *)... | tests.cpp:58:41:58:46 | call to getenv | tests.cpp:58:41:58:62 | (const char_type *)... | This operation potentially exposes sensitive system data from $@. | tests.cpp:58:41:58:46 | call to getenv | call to getenv |
| tests.cpp:59:43:59:48 | call to getenv | tests.cpp:59:43:59:48 | call to getenv | tests.cpp:59:43:59:48 | call to getenv | This operation potentially exposes sensitive system data from $@. | tests.cpp:59:43:59:48 | call to getenv | call to getenv |
| tests.cpp:59:43:59:64 | (const char *)... | tests.cpp:59:43:59:48 | call to getenv | tests.cpp:59:43:59:64 | (const char *)... | This operation potentially exposes sensitive system data from $@. | tests.cpp:59:43:59:48 | call to getenv | call to getenv |
| tests.cpp:88:15:88:17 | msg | tests.cpp:97:13:97:18 | call to getenv | tests.cpp:88:15:88:17 | msg | This operation potentially exposes sensitive system data from $@. | tests.cpp:97:13:97:18 | call to getenv | call to getenv |
| tests.cpp:97:13:97:18 | call to getenv | tests.cpp:97:13:97:18 | call to getenv | tests.cpp:97:13:97:18 | call to getenv | This operation potentially exposes sensitive system data from $@. | tests.cpp:97:13:97:18 | call to getenv | call to getenv |
| tests.cpp:97:13:97:34 | (const char *)... | tests.cpp:97:13:97:18 | call to getenv | tests.cpp:97:13:97:34 | (const char *)... | This operation potentially exposes sensitive system data from $@. | tests.cpp:97:13:97:18 | call to getenv | call to getenv |
| tests.cpp:111:15:111:17 | tmp | tests.cpp:131:14:131:19 | call to getenv | tests.cpp:111:15:111:17 | tmp | This operation potentially exposes sensitive system data from $@. | tests.cpp:131:14:131:19 | call to getenv | call to getenv |
| tests.cpp:119:7:119:12 | (const char *)... | tests.cpp:132:14:132:19 | call to getenv | tests.cpp:119:7:119:12 | (const char *)... | This operation potentially exposes sensitive system data from $@. | tests.cpp:132:14:132:19 | call to getenv | call to getenv |
| tests.cpp:124:15:124:17 | msg | tests.cpp:133:14:133:19 | call to getenv | tests.cpp:124:15:124:17 | msg | This operation potentially exposes sensitive system data from $@. | tests.cpp:133:14:133:19 | call to getenv | call to getenv |
| tests.cpp:133:14:133:19 | call to getenv | tests.cpp:133:14:133:19 | call to getenv | tests.cpp:133:14:133:19 | call to getenv | This operation potentially exposes sensitive system data from $@. | tests.cpp:133:14:133:19 | call to getenv | call to getenv |
| tests.cpp:133:14:133:35 | (const char *)... | tests.cpp:133:14:133:19 | call to getenv | tests.cpp:133:14:133:35 | (const char *)... | This operation potentially exposes sensitive system data from $@. | tests.cpp:133:14:133:19 | call to getenv | call to getenv |
| tests_passwd.cpp:18:29:18:31 | pwd | tests_passwd.cpp:16:8:16:15 | call to getpwnam | tests_passwd.cpp:18:29:18:31 | pwd | This operation potentially exposes sensitive system data from $@. | tests_passwd.cpp:16:8:16:15 | call to getpwnam | call to getpwnam |
| tests_passwd.cpp:19:26:19:28 | pwd | tests_passwd.cpp:16:8:16:15 | call to getpwnam | tests_passwd.cpp:19:26:19:28 | pwd | This operation potentially exposes sensitive system data from $@. | tests_passwd.cpp:16:8:16:15 | call to getpwnam | call to getpwnam |

View File

@@ -0,0 +1 @@
Security/CWE/CWE-497/PotentiallyExposedSystemData.ql

View File

@@ -0,0 +1,134 @@
// test cases for rule CWE-497
// library functions etc
#include "tests.h"
typedef struct {} FILE;
FILE *stdout;
int puts(const char *s);
int printf(const char *format, ...);
int sprintf(char *s, const char *format, ...);
int snprintf(char *s, size_t n, const char *format, ...);
size_t strlen(const char *s);
char *getenv(const char *name);
extern std::ostream someotherostream;
#define NULL (0)
// test cases
void test1()
{
std::ostream cout_copy = std::cout;
std::cout << getenv("SECRET_TOKEN"); // BAD: outputs SECRET_TOKEN environment variable
std::cerr << getenv("SECRET_TOKEN"); // BAD: outputs SECRET_TOKEN environment variable
std::clog << getenv("SECRET_TOKEN"); // BAD: outputs SECRET_TOKEN environment variable
someotherostream << getenv("SECRET_TOKEN"); // GOOD: not output
cout_copy << getenv("SECRET_TOKEN"); // BAD: outputs SECRET_TOKEN environment variable [NOT DETECTED]
std::cout << getenv("USERPROFILE"); // BAD: outputs PATH environment variable [NOT DETECTED]
std::cout << getenv("PATH"); // BAD: outputs PATH environment variable [NOT DETECTED]
std::cout.write(getenv("SECRET_TOKEN"), strlen(getenv("SECRET_TOKEN"))); // BAD: outputs SECRET_TOKEN environment variable
(std::cout << "SECRET_TOKEN = ").write(getenv("SECRET_TOKEN"), strlen(getenv("SECRET_TOKEN"))); // BAD: outputs SECRET_TOKEN environment variable
std::cout.write("SECRET_TOKEN = ", 7) << getenv("SECRET_TOKEN"); // BAD: outputs SECRET_TOKEN environment variable
}
char *global_token = getenv("SECRET_TOKEN");
char *global_other = "Hello, world!";
void test2(bool cond)
{
char *maybe;
maybe = cond ? global_token : global_other;
printf("token = '%s'\n", global_token); // BAD: outputs SECRET_TOKEN environment variable [NOT DETECTED]
printf("other = '%s'\n", global_other);
printf("maybe = '%s'\n", maybe); // BAD: may output SECRET_TOKEN environment variable [NOT DETECTED]
}
void test3()
{
char *path_string = getenv("PATH");
char buf[4096];
// ...
snprintf(buf, 4096, "invalid path '%s'\n", path_string);
puts(buf); // BAD: outputs PATH environment variable [NOT DETECTED]
}
void myOutputFn(const char *msg)
{
printf("%s", msg);
}
void myOtherFn(const char *msg)
{
}
void test4()
{
myOutputFn(getenv("SECRET_TOKEN")); // BAD: outputs the SECRET_TOKEN environment variable
myOtherFn(getenv("SECRET_TOKEN")); // GOOD: does not output anything.
}
void myOutputFn2(const char *msg)
{
msg = "";
printf("%s", msg);
}
void myOutputFn3(const char *msg)
{
const char *tmp = msg;
printf("%s", tmp);
}
void myOutputFn4(const char *msg)
{
char buffer[4096];
sprintf(buffer, "log: %s\n", msg);
puts(buffer);
}
void myOutputFn5(const char *msg)
{
printf("%s", msg);
msg = "";
}
void test5()
{
myOutputFn2(getenv("SECRET_TOKEN")); // GOOD: myOutputFn2 doesn't actually output the parameter
myOutputFn3(getenv("SECRET_TOKEN")); // BAD: outputs the SECRET_TOKEN environment variable
myOutputFn4(getenv("SECRET_TOKEN")); // BAD: outputs the SECRET_TOKEN environment variable
myOutputFn5(getenv("SECRET_TOKEN")); // BAD: outputs the SECRET_TOKEN environment variable
}

View File

@@ -0,0 +1,25 @@
typedef unsigned long size_t;
namespace std
{
typedef size_t streamsize;
template<class charT> struct char_traits;
template <class charT, class traits = char_traits<charT> >
class basic_ostream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
public:
typedef charT char_type;
basic_ostream<charT,traits>& write(const char_type* s, streamsize n);
basic_ostream<charT, traits>& operator<<(int n);
};
template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const charT*);
typedef basic_ostream<char> ostream;
extern ostream cout;
extern ostream cerr;
extern ostream clog;
}

View File

@@ -2,24 +2,24 @@
// library functions etc
#include "tests.h"
char *getenv(const char *name);
char *strcpy(char *s1, const char *s2);
namespace std
{
template<class charT> struct char_traits;
template <class charT, class traits = char_traits<charT> >
class basic_ostream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
public:
};
template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const charT*);
typedef basic_ostream<char> ostream;
extern ostream cout;
}
int socket(int p1, int p2, int p3);
void send(int sock, const char *buffer, int p3, int p4);
@@ -63,10 +63,12 @@ void test1()
send(sock, getenv("HOME"), val(), val()); // BAD
send(sock, getenv("PATH"), val(), val()); // BAD
send(sock, getenv("USERNAME"), val(), val()); // BAD
send(sock, getenv("APP_PASSWORD"), val(), val()); // BAD
send(sock, getenv("HARMLESS"), val(), val()); // GOOD: harmless information
send(sock, "HOME", val(), val()); // GOOD: not system data
send(sock, "PATH", val(), val()); // GOOD: not system data
send(sock, "USERNAME", val(), val()); // GOOD: not system data
send(sock, "APP_PASSWORD", val(), val()); // GOOD: not system data
send(sock, "HARMLESS", val(), val()); // GOOD: not system data
// tests for `mysql_get_client_info`, including via a global

View File

@@ -0,0 +1,21 @@
int printf(const char *format, ...);
struct passwd {
char *pw_passwd;
char *pw_dir;
// ...
};
struct passwd *getpwnam(const char *name);
void test6(char *username)
{
passwd *pwd;
pwd = getpwnam(username);
printf("pw_passwd = %s\n", pwd->pw_passwd); // BAD
printf("pw_dir = %s\n", pwd->pw_dir); // BAD
printf("sizeof(passwd) = %i\n", sizeof(passwd)); // GOOD
}

View File

@@ -0,0 +1,84 @@
typedef unsigned long size_t;
size_t strlen(const char *s);
char *getenv(const char *name);
#define AF_INET (2)
#define SOCK_STREAM (1)
struct sockaddr {
int sa_family;
// ...
};
int socket(int domain, int type, int protocol);
int connect(int socket, const struct sockaddr *address, size_t address_len);
size_t send(int socket, const void *buffer, size_t length, int flags);
int write(int handle, const void *buffer, size_t length);
void test_sockets1()
{
int sockfd;
sockaddr addr_remote;
char *msg = "Hello, world!";
char *path = getenv("PATH");
// create socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) return;
// connect socket to a remote address
addr_remote.sa_family = AF_INET;
// ...
if (connect(sockfd, &addr_remote, sizeof(addr_remote)) != 0) return;
// send something using 'send'
if (send(sockfd, msg, strlen(msg) + 1, 0) < 0) return; // GOOD
if (send(sockfd, path, strlen(path) + 1, 0) < 0) return; // BAD
// send something using 'write'
if (write(sockfd, msg, strlen(msg) + 1) < 0) return; // GOOD
if (write(sockfd, path, strlen(path) + 1) < 0) return; // BAD
// clean up
// ...
}
int mksocket()
{
int fd;
fd = socket(AF_INET, SOCK_STREAM, 0);
return fd;
}
void test_sockets2()
{
int sockfd;
sockaddr addr_remote;
char *msg = "Hello, world!";
char *path = getenv("PATH");
// create socket
sockfd = mksocket();
if (sockfd < 0) return;
// connect socket to a remote address
addr_remote.sa_family = AF_INET;
// ...
if (connect(sockfd, &addr_remote, sizeof(addr_remote)) != 0) return;
// send something using 'send'
if (send(sockfd, msg, strlen(msg) + 1, 0) < 0) return; // GOOD
if (send(sockfd, path, strlen(path) + 1, 0) < 0) return; // BAD
// send something using 'write'
if (write(sockfd, msg, strlen(msg) + 1) < 0) return; // GOOD
if (write(sockfd, path, strlen(path) + 1) < 0) return; // BAD
// clean up
// ...
}

View File

@@ -0,0 +1,41 @@
typedef unsigned long size_t;
typedef signed long ssize_t;
void *malloc(size_t size);
#define NULL (0)
int printf(const char *format, ...);
size_t strlen(const char *s);
int get_fd();
int write(int handle, const void *buffer, size_t length);
long sysconf(int name);
#define _SC_CHILD_MAX (2)
size_t confstr(int name, char *buffer, size_t length);
#define _CS_PATH (1)
void test_sc_1()
{
int value = sysconf(_SC_CHILD_MAX);
printf("_SC_CHILD_MAX = %i\n", _SC_CHILD_MAX); // GOOD
printf("_SC_CHILD_MAX = %i\n", value); // BAD [NOT DETECTED]
}
void test_sc_2()
{
char *pathbuf;
size_t n;
n = confstr(_CS_PATH, NULL, (size_t)0);
pathbuf = (char *)malloc(n);
if (pathbuf != NULL)
{
confstr(_CS_PATH, pathbuf, n);
printf("path: %s", pathbuf); // BAD [NOT DETECTED]
write(get_fd(), pathbuf, strlen(pathbuf)); // BAD
}
}

View File

@@ -161,8 +161,13 @@ class IRBlock extends IRBlockBase {
*/
pragma[noinline]
final IRBlock dominanceFrontier() {
this.dominates(result.getAPredecessor()) and
not this.strictlyDominates(result)
this.getASuccessor() = result and
not this.immediatelyDominates(result)
or
exists(IRBlock prev | result = prev.dominanceFrontier() |
this.immediatelyDominates(prev) and
not this.immediatelyDominates(result)
)
}
/**
@@ -201,8 +206,13 @@ class IRBlock extends IRBlockBase {
*/
pragma[noinline]
final IRBlock postDominanceFrontier() {
this.postDominates(result.getASuccessor()) and
not this.strictlyPostDominates(result)
this.getAPredecessor() = result and
not this.immediatelyPostDominates(result)
or
exists(IRBlock prev | result = prev.postDominanceFrontier() |
this.immediatelyPostDominates(prev) and
not this.immediatelyPostDominates(result)
)
}
/**

View File

@@ -161,8 +161,13 @@ class IRBlock extends IRBlockBase {
*/
pragma[noinline]
final IRBlock dominanceFrontier() {
this.dominates(result.getAPredecessor()) and
not this.strictlyDominates(result)
this.getASuccessor() = result and
not this.immediatelyDominates(result)
or
exists(IRBlock prev | result = prev.dominanceFrontier() |
this.immediatelyDominates(prev) and
not this.immediatelyDominates(result)
)
}
/**
@@ -201,8 +206,13 @@ class IRBlock extends IRBlockBase {
*/
pragma[noinline]
final IRBlock postDominanceFrontier() {
this.postDominates(result.getASuccessor()) and
not this.strictlyPostDominates(result)
this.getAPredecessor() = result and
not this.immediatelyPostDominates(result)
or
exists(IRBlock prev | result = prev.postDominanceFrontier() |
this.immediatelyPostDominates(prev) and
not this.immediatelyPostDominates(result)
)
}
/**

View File

@@ -1,178 +1,15 @@
#!/usr/bin/python3
import json
import os
import os.path
import shlex
import subprocess
import sys
import tempfile
import os.path
import subprocess
# Add Model as Data script directory to sys.path.
gitroot = subprocess.check_output(["git", "rev-parse", "--show-toplevel"]).decode("utf-8").strip()
madpath = os.path.join(gitroot, "misc/scripts/models-as-data/")
sys.path.append(madpath)
def printHelp():
print("""Usage:
GenerateFlowModel.py <library-database> <outputQll> [--with-sinks] [--with-sources] [--with-summaries]
import generate_flow_model as model
This generates summary, source and sink models for the code in the database.
The files will be placed in `java/ql/lib/semmle/code/java/frameworks/<outputQll>` where
outputQll is the name (and path) of the output QLL file. Usually, models are grouped by their
respective frameworks.
Which models are generated is controlled by the flags:
--with-sinks
--with-sources
--with-summaries
If none of these flags are specified, all models are generated.
Example invocations:
$ GenerateFlowModel.py /tmp/dbs/apache_commons-codec_45649c8 "apache/Codec.qll"
$ GenerateFlowModel.py /tmp/dbs/jdk15_db "javase/jdk_sinks.qll" --with-sinks
Requirements: `codeql` should both appear on your path.
""")
if any(s == "--help" for s in sys.argv):
printHelp()
sys.exit(0)
generateSinks = False
generateSources = False
generateSummaries = False
if "--with-sinks" in sys.argv:
sys.argv.remove("--with-sinks")
generateSinks = True
if "--with-sources" in sys.argv:
sys.argv.remove("--with-sources")
generateSources = True
if "--with-summaries" in sys.argv:
sys.argv.remove("--with-summaries")
generateSummaries = True
if not generateSinks and not generateSources and not generateSummaries:
generateSinks = generateSources = generateSummaries = True
if len(sys.argv) != 3:
printHelp()
sys.exit(1)
codeQlRoot = subprocess.check_output(
["git", "rev-parse", "--show-toplevel"]).decode("utf-8").strip()
targetQll = sys.argv[2]
if not targetQll.endswith(".qll"):
targetQll += ".qll"
filename = os.path.basename(targetQll)
shortname = filename[:-4]
generatedFrameworks = os.path.join(
codeQlRoot, "java/ql/lib/semmle/code/java/frameworks/")
frameworkTarget = os.path.join(generatedFrameworks, targetQll)
workDir = tempfile.mkdtemp()
os.makedirs(generatedFrameworks, exist_ok=True)
def runQuery(infoMessage, query):
print("########## Querying " + infoMessage + "...")
database = sys.argv[1]
queryFile = os.path.join(os.path.dirname(
__file__), query)
resultBqrs = os.path.join(workDir, "out.bqrs")
cmd = ['codeql', 'query', 'run', queryFile, '--database',
database, '--output', resultBqrs, '--threads', '8']
ret = subprocess.call(cmd)
if ret != 0:
print("Failed to generate " + infoMessage +
". Failed command was: " + shlex.join(cmd))
sys.exit(1)
return readRows(resultBqrs)
def readRows(bqrsFile):
generatedJson = os.path.join(workDir, "out.json")
cmd = ['codeql', 'bqrs', 'decode', bqrsFile,
'--format=json', '--output', generatedJson]
ret = subprocess.call(cmd)
if ret != 0:
print("Failed to decode BQRS. Failed command was: " + shlex.join(cmd))
sys.exit(1)
with open(generatedJson) as f:
results = json.load(f)
try:
results['#select']['tuples']
except KeyError:
print('Unexpected JSON output - no tuples found')
exit(1)
rows = ""
for (row) in results['#select']['tuples']:
rows += " \"" + row[0] + "\",\n"
return rows[:-2]
def asCsvModel(superclass, kind, rows):
classTemplate = """
private class {0}{1}Csv extends {2} {{
override predicate row(string row) {{
row =
[
{3}
]
}}
}}
"""
if rows.strip() == "":
return ""
return classTemplate.format(shortname[0].upper() + shortname[1:], kind.capitalize(), superclass, rows)
if generateSummaries:
summaryRows = runQuery("summary models", "CaptureSummaryModels.ql")
summaryCsv = asCsvModel("SummaryModelCsv", "summary", summaryRows)
else:
summaryCsv = ""
if generateSinks:
sinkRows = runQuery("sink models", "CaptureSinkModels.ql")
sinkCsv = asCsvModel("SinkModelCsv", "sinks", sinkRows)
else:
sinkCsv = ""
if generateSources:
sourceRows = runQuery("source models", "CaptureSourceModels.ql")
sourceCsv = asCsvModel("SourceModelCsv", "sources", sourceRows)
else:
sourceCsv = ""
qllTemplate = """
/** Definitions of taint steps in the {0} framework */
import java
private import semmle.code.java.dataflow.ExternalFlow
{1}
{2}
{3}
"""
qllContents = qllTemplate.format(shortname, sinkCsv, sourceCsv, summaryCsv)
with open(frameworkTarget, "w") as frameworkQll:
frameworkQll.write(qllContents)
cmd = ['codeql', 'query', 'format', '--in-place', frameworkTarget]
ret = subprocess.call(cmd)
if ret != 0:
print("Failed to format query. Failed command was: " + shlex.join(cmd))
sys.exit(1)
print("")
print("CSV model written to " + frameworkTarget)
language = "java"
model.Generator.make(language).run()

View File

@@ -192,8 +192,8 @@ class Test {
sink(StringUtils.rotate(taint(), 0)); // $hasTaintFlow
sink(StringUtils.split(taint())); // $hasTaintFlow
sink(StringUtils.split(taint(), ' ')); // $hasTaintFlow
sink(StringUtils.split(taint(), " ,; // $hasTaintFlow")); // $hasTaintFlow
sink(StringUtils.split(taint(), " ,; // $hasTaintFlow", 0)); // $hasTaintFlow
sink(StringUtils.split(taint(), " ,;")); // $hasTaintFlow
sink(StringUtils.split(taint(), " ,;", 0)); // $hasTaintFlow
sink(StringUtils.splitByCharacterType(taint())); // $hasTaintFlow
sink(StringUtils.splitByCharacterTypeCamelCase(taint())); // $hasTaintFlow
sink(StringUtils.splitByWholeSeparator(taint(), "separator")); // $hasTaintFlow

View File

@@ -14,6 +14,7 @@
*/
import javascript
import semmle.javascript.security.IncompleteBlacklistSanitizer as IncompleteBlacklistSanitizer
/** A URL scheme that can be used to represent executable code. */
class DangerousScheme extends string {
@@ -55,6 +56,21 @@ DataFlow::SourceNode schemeOf(DataFlow::Node url) {
)
}
/**
* A chain of replace calls that replaces one or more dangerous schemes.
*/
class SchemeReplacementChain extends IncompleteBlacklistSanitizer::StringReplaceCallSequence {
SchemeReplacementChain() { this.getAMember().getAReplacedString() instanceof DangerousScheme }
/**
* Gets the source node that the replacement happens on.
* The result is the receiver of the first call in the chain.
*/
DataFlow::Node getReplacementSource() {
result = this.getReceiver+() and not result instanceof DataFlow::MethodCallNode
}
}
/** Gets a data-flow node that checks `nd` against the given `scheme`. */
DataFlow::Node schemeCheck(DataFlow::Node nd, DangerousScheme scheme) {
// check of the form `nd.startsWith(scheme)`
@@ -73,6 +89,11 @@ DataFlow::Node schemeCheck(DataFlow::Node nd, DangerousScheme scheme) {
schemeOf(nd).flowsTo(candidate)
)
or
exists(SchemeReplacementChain chain | result = chain |
scheme = chain.getAMember().getAReplacedString() and
nd = chain.getReplacementSource()
)
or
// propagate through trimming, case conversion, and regexp replace
exists(DataFlow::MethodCallNode stringop |
stringop.getMethodName().matches("trim%") or

View File

@@ -11,3 +11,5 @@
| IncompleteUrlSchemeCheck.js:87:7:87:40 | /^(java ... scheme) | This check does not consider vbscript:. |
| IncompleteUrlSchemeCheck.js:94:10:94:15 | scheme | This check does not consider vbscript:. |
| IncompleteUrlSchemeCheck.js:104:6:104:39 | /^(java ... scheme) | This check does not consider vbscript:. |
| IncompleteUrlSchemeCheck.js:110:12:112:29 | url // ... :/, "") | This check does not consider vbscript:. |
| IncompleteUrlSchemeCheck.js:124:11:124:34 | url.rep ... :/, "") | This check does not consider vbscript:. |

View File

@@ -105,3 +105,26 @@ function test14(url) {
return "about:blank";
return url;
}
function chain1(url) {
return url // NOT OK
.replace(/javascript:/, "")
.replace(/data:/, "");
}
function chain2(url) {
return url // OK
.replace(/javascript:/, "")
.replace(/data:/, "")
.replace(/vbscript:/, "");
}
function chain3(url) {
url = url.replace(/javascript:/, "")
url = url.replace(/data:/, ""); // NOT OK
return url;
}
function chain4(url) {
return url.replace(/(javascript|data):/, ""); // NOT OK - but not flagged [INCONSISTENCY]
}

View File

@@ -0,0 +1,203 @@
#!/usr/bin/python3
import json
import os
import os.path
import shlex
import subprocess
import sys
import tempfile
class Generator:
def __init__ (self, language):
self.language = language
self.generateSinks = False
self.generateSources = False
self.generateSummaries = False
self.dryRun = False
def printHelp(self):
print(f"""Usage:
python3 GenerateFlowModel.py <library-database> <outputQll> [--with-sinks] [--with-sources] [--with-summaries] [--dry-run]
This generates summary, source and sink models for the code in the database.
The files will be placed in `{self.language}/ql/lib/semmle/code/{self.language}/frameworks/<outputQll>` where
outputQll is the name (and path) of the output QLL file. Usually, models are grouped by their
respective frameworks.
Which models are generated is controlled by the flags:
--with-sinks
--with-sources
--with-summaries
If none of these flags are specified, all models are generated.
--dry-run: Only run the queries, but don't write to file.
Example invocations:
$ python3 GenerateFlowModel.py /tmp/dbs/my_library_db "mylibrary/Framework.qll"
$ python3 GenerateFlowModel.py /tmp/dbs/my_library_db "mylibrary/FrameworkSinks.qll" --with-sinks
Requirements: `codeql` should both appear on your path.
""")
def setenvironment(self, target, database):
self.codeQlRoot = subprocess.check_output(["git", "rev-parse", "--show-toplevel"]).decode("utf-8").strip()
if not target.endswith(".qll"):
target += ".qll"
self.filename = os.path.basename(target)
self.shortname = self.filename[:-4]
self.database = database
self.generatedFrameworks = os.path.join(
self.codeQlRoot, f"{self.language}/ql/lib/semmle/code/{self.language}/frameworks/")
self.frameworkTarget = os.path.join(self.generatedFrameworks, target)
self.workDir = tempfile.mkdtemp()
os.makedirs(self.generatedFrameworks, exist_ok=True)
@staticmethod
def make(language):
generator = Generator(language)
if any(s == "--help" for s in sys.argv):
generator.printHelp()
sys.exit(0)
if "--with-sinks" in sys.argv:
sys.argv.remove("--with-sinks")
generator.generateSinks = True
if "--with-sources" in sys.argv:
sys.argv.remove("--with-sources")
generator.generateSources = True
if "--with-summaries" in sys.argv:
sys.argv.remove("--with-summaries")
generator.generateSummaries = True
if "--dry-run" in sys.argv:
sys.argv.remove("--dry-run")
generator.dryRun = True
if not generator.generateSinks and not generator.generateSources and not generator.generateSummaries:
generator.generateSinks = generator.generateSources = generator.generateSummaries = True
if len(sys.argv) != 3:
generator.printHelp()
sys.exit(1)
generator.setenvironment(sys.argv[2], sys.argv[1])
return generator
def runQuery(self, infoMessage, query):
print("########## Querying " + infoMessage + "...")
queryFile = os.path.join(self.codeQlRoot, f"{self.language}/ql/src/utils/model-generator", query)
resultBqrs = os.path.join(self.workDir, "out.bqrs")
cmd = ['codeql', 'query', 'run', queryFile, '--database',
self.database, '--output', resultBqrs, '--threads', '8']
ret = subprocess.call(cmd)
if ret != 0:
print("Failed to generate " + infoMessage +
". Failed command was: " + shlex.join(cmd))
sys.exit(1)
return self.readRows(resultBqrs)
def readRows(self, bqrsFile):
generatedJson = os.path.join(self.workDir, "out.json")
cmd = ['codeql', 'bqrs', 'decode', bqrsFile,
'--format=json', '--output', generatedJson]
ret = subprocess.call(cmd)
if ret != 0:
print("Failed to decode BQRS. Failed command was: " + shlex.join(cmd))
sys.exit(1)
with open(generatedJson) as f:
results = json.load(f)
try:
results['#select']['tuples']
except KeyError:
print('Unexpected JSON output - no tuples found')
exit(1)
rows = ""
for (row) in results['#select']['tuples']:
rows += " \"" + row[0] + "\",\n"
return rows[:-2]
def asCsvModel(self, superclass, kind, rows):
classTemplate = """
private class {0}{1}Csv extends {2} {{
override predicate row(string row) {{
row =
[
{3}
]
}}
}}
"""
if rows.strip() == "":
return ""
return classTemplate.format(self.shortname[0].upper() + self.shortname[1:], kind.capitalize(), superclass, rows)
def makeContent(self):
if self.generateSummaries:
summaryRows = self.runQuery("summary models", "CaptureSummaryModels.ql")
summaryCsv = self.asCsvModel("SummaryModelCsv", "summary", summaryRows)
else:
summaryCsv = ""
if self.generateSinks:
sinkRows = self.runQuery("sink models", "CaptureSinkModels.ql")
sinkCsv = self.asCsvModel("SinkModelCsv", "sinks", sinkRows)
else:
sinkCsv = ""
if self.generateSources:
sourceRows = self.runQuery("source models", "CaptureSourceModels.ql")
sourceCsv = self.asCsvModel("SourceModelCsv", "sources", sourceRows)
else:
sourceCsv = ""
return f"""
/** Definitions of taint steps in the {self.shortname} framework */
import {self.language}
private import semmle.code.{self.language}.dataflow.ExternalFlow
{sinkCsv}
{sourceCsv}
{summaryCsv}
"""
def save(self, content):
with open(self.frameworkTarget, "w") as frameworkQll:
frameworkQll.write(content)
cmd = ['codeql', 'query', 'format', '--in-place', self.frameworkTarget]
ret = subprocess.call(cmd)
if ret != 0:
print("Failed to format query. Failed command was: " + shlex.join(cmd))
sys.exit(1)
print("")
print("CSV model written to " + self.frameworkTarget)
def run(self):
content = self.makeContent()
if self.dryRun:
print("CSV Models generated, but not written to file.")
sys.exit(0)
self.save(content)

View File

@@ -6,7 +6,8 @@
* directed and labeled; they specify how the components represented by nodes relate to each other.
*/
private import python
// Importing python under the `py` namespace to avoid importing `CallNode` from `Flow.qll` and thereby having a naming conflict with `API::CallNode`.
private import python as py
import semmle.python.dataflow.new.DataFlow
/**
@@ -39,6 +40,30 @@ module API {
)
}
/**
* Gets a data-flow node corresponding to the right-hand side of a definition of the API
* component represented by this node.
*
* For example, in the property write `foo.bar = x`, variable `x` is the the right-hand side
* of a write to the `bar` property of `foo`.
*
* Note that for parameters, it is the arguments flowing into that parameter that count as
* right-hand sides of the definition, not the declaration of the parameter itself.
* Consequently, in :
* ```python
* from mypkg import foo;
* foo.bar(x)
* ```
* `x` is the right-hand side of a definition of the first parameter of `bar` from the `mypkg.foo` module.
*/
DataFlow::Node getARhs() { Impl::rhs(this, result) }
/**
* Gets a data-flow node that may interprocedurally flow to the right-hand side of a definition
* of the API component represented by this node.
*/
DataFlow::Node getAValueReachingRhs() { result = Impl::trackDefNode(this.getARhs()) }
/**
* Gets an immediate use of the API component represented by this node.
*
@@ -55,7 +80,7 @@ module API {
/**
* Gets a call to the function represented by this API component.
*/
DataFlow::CallCfgNode getACall() { result = this.getReturn().getAnImmediateUse() }
CallNode getACall() { result = this.getReturn().getAnImmediateUse() }
/**
* Gets a node representing member `m` of this API component.
@@ -92,6 +117,29 @@ module API {
*/
Node getReturn() { result = this.getASuccessor(Label::return()) }
/**
* Gets a node representing the `i`th parameter of the function represented by this node.
*
* This predicate may have multiple results when there are multiple invocations of this API component.
* Consider using `getAnInvocation()` if there is a need to distingiush between individual calls.
*/
Node getParameter(int i) { result = this.getASuccessor(Label::parameter(i)) }
/**
* Gets the node representing the keyword parameter `name` of the function represented by this node.
*
* This predicate may have multiple results when there are multiple invocations of this API component.
* Consider using `getAnInvocation()` if there is a need to distingiush between individual calls.
*/
Node getKeywordParameter(string name) {
result = this.getASuccessor(Label::keywordParameter(name))
}
/**
* Gets the number of parameters of the function represented by this node.
*/
int getNumParameter() { result = max(int s | exists(this.getParameter(s))) + 1 }
/**
* Gets a node representing a subclass of the class represented by this node.
*/
@@ -137,7 +185,7 @@ module API {
/**
* Gets the data-flow node that gives rise to this node, if any.
*/
DataFlow::Node getInducingNode() { this = Impl::MkUse(result) }
DataFlow::Node getInducingNode() { this = Impl::MkUse(result) or this = Impl::MkDef(result) }
/**
* Holds if this element is at the specified location.
@@ -210,6 +258,17 @@ module API {
}
}
/** A node corresponding to the rhs of an API component. */
class Def extends Node, Impl::TDef {
override string toString() {
exists(string type | this = Impl::MkDef(_) and type = "Def " |
result = type + this.getPath()
or
not exists(this.getPath()) and result = type + "with no path"
)
}
}
/** Gets the root node. */
Root root() { any() }
@@ -225,6 +284,62 @@ module API {
/** Gets a node corresponding to the built-in with the given name, if any. */
Node builtin(string n) { result = moduleImport("builtins").getMember(n) }
/**
* An `CallCfgNode` that is connected to the API graph.
*
* Can be used to reason about calls to an external API in which the correlation between
* parameters and/or return values must be retained.
*
* The member predicates `getParameter`, `getKeywordParameter`, `getReturn`, and `getInstance` mimic
* the corresponding predicates from `API::Node`. These are guaranteed to exist and be unique to this call.
*/
class CallNode extends DataFlow::CallCfgNode {
API::Node callee;
CallNode() { this = callee.getReturn().getAnImmediateUse() }
/** Gets the API node for the `i`th parameter of this invocation. */
pragma[nomagic]
Node getParameter(int i) {
result = callee.getParameter(i) and
result = this.getAParameterCandidate(i)
}
/**
* Gets an API node where a RHS of the node is the `i`th argument to this call.
*/
pragma[noinline]
private Node getAParameterCandidate(int i) { result.getARhs() = this.getArg(i) }
/** Gets the API node for a parameter of this invocation. */
Node getAParameter() { result = this.getParameter(_) }
/** Gets the API node for the keyword parameter `name` of this invocation. */
Node getKeywordParameter(string name) {
result = callee.getKeywordParameter(name) and
result = this.getAKeywordParameterCandidate(name)
}
/** Gets the API node for the parameter that has index `i` or has keyword `name`. */
bindingset[i, name]
Node getParameter(int i, string name) {
result = this.getParameter(i)
or
result = this.getKeywordParameter(name)
}
pragma[noinline]
private Node getAKeywordParameterCandidate(string name) {
result.getARhs() = this.getArgByName(name)
}
/** Gets the API node for the return value of this call. */
Node getReturn() {
result = callee.getReturn() and
result.getAnImmediateUse() = this
}
}
/**
* Provides the actual implementation of API graphs, cached for performance.
*
@@ -312,23 +427,26 @@ module API {
/** An abstract representative for imports of the module called `name`. */
MkModuleImport(string name) {
// Ignore the following module name for Python 2, as we alias `__builtin__` to `builtins` elsewhere
(name != "__builtin__" or major_version() = 3) and
(name != "__builtin__" or py::major_version() = 3) and
(
imports(_, name)
or
// When we `import foo.bar.baz` we want to create API graph nodes also for the prefixes
// `foo` and `foo.bar`:
name = any(ImportExpr e | not e.isRelative()).getAnImportedModuleName()
name = any(py::ImportExpr e | not e.isRelative()).getAnImportedModuleName()
)
or
// The `builtins` module should always be implicitly available
name = "builtins"
} or
/** A use of an API member at the node `nd`. */
MkUse(DataFlow::Node nd) { use(_, _, nd) }
MkUse(DataFlow::Node nd) { use(_, _, nd) } or
MkDef(DataFlow::Node nd) { rhs(_, _, nd) }
class TUse = MkModuleImport or MkUse;
class TDef = MkDef;
/**
* Holds if the dotted module name `sub` refers to the `member` member of `base`.
*
@@ -351,7 +469,7 @@ module API {
* Ignores relative imports, such as `from ..foo.bar import baz`.
*/
private predicate imports(DataFlow::Node imp, string name) {
exists(ImportExprNode iexpr |
exists(py::ImportExprNode iexpr |
imp.asCfgNode() = iexpr and
not iexpr.getNode().isRelative() and
name = iexpr.getNode().getImportedModuleName()
@@ -374,13 +492,55 @@ module API {
*
* `moduleImport("foo").getMember("bar")`
*/
private TApiNode potential_import_star_base(Scope s) {
private TApiNode potential_import_star_base(py::Scope s) {
exists(DataFlow::Node n |
n.asCfgNode() = ImportStar::potentialImportStarBase(s) and
use(result, n)
)
}
/**
* Holds if `rhs` is the right-hand side of a definition of a node that should have an
* incoming edge from `base` labeled `lbl` in the API graph.
*/
cached
predicate rhs(TApiNode base, Label::ApiLabel lbl, DataFlow::Node rhs) {
exists(DataFlow::Node def, DataFlow::LocalSourceNode pred |
rhs(base, def) and pred = trackDefNode(def)
|
// from `x` to a definition of `x.prop`
exists(DataFlow::AttrWrite aw | aw = pred.getAnAttributeWrite() |
lbl = Label::memberFromRef(aw) and
rhs = aw.getValue()
)
or
// TODO: I had expected `DataFlow::AttrWrite` to contain the attribute writes from a dict, that's how JS works.
exists(py::Dict dict, py::KeyValuePair item |
dict = pred.asExpr() and
dict.getItem(_) = item and
lbl = Label::member(item.getKey().(py::StrConst).getS()) and
rhs.asExpr() = item.getValue()
)
or
exists(py::CallableExpr fn | fn = pred.asExpr() |
not fn.getInnerScope().isAsync() and
lbl = Label::return() and
exists(py::Return ret |
rhs.asExpr() = ret.getValue() and
ret.getScope() = fn.getInnerScope()
)
)
)
or
argumentPassing(base, lbl, rhs)
or
exists(DataFlow::LocalSourceNode src, DataFlow::AttrWrite aw |
use(base, src) and aw = trackUseNode(src).getAnAttributeWrite() and rhs = aw.getValue()
|
lbl = Label::memberFromRef(aw)
)
}
/**
* Holds if `ref` is a use of a node that should have an incoming edge from `base` labeled
* `lbl` in the API graph.
@@ -399,7 +559,7 @@ module API {
|
// Referring to an attribute on a node that is a use of `base`:
lbl = Label::memberFromRef(ref) and
ref = pred.getAnAttributeReference()
ref = pred.getAnAttributeRead()
or
// Calling a node that is a use of `base`
lbl = Label::return() and
@@ -408,7 +568,7 @@ module API {
// Subclassing a node
lbl = Label::subclass() and
exists(DataFlow::Node superclass | pred.flowsTo(superclass) |
ref.asExpr().(ClassExpr).getABase() = superclass.asExpr()
ref.asExpr().(py::ClassExpr).getABase() = superclass.asExpr()
)
or
// awaiting
@@ -419,12 +579,26 @@ module API {
)
)
or
exists(DataFlow::Node def, py::CallableExpr fn |
rhs(base, def) and fn = trackDefNode(def).asExpr()
|
exists(int i |
lbl = Label::parameter(i) and
ref.asExpr() = fn.getInnerScope().getArg(i)
)
or
exists(string name |
lbl = Label::keywordParameter(name) and
ref.asExpr() = fn.getInnerScope().getArgByName(name)
)
)
or
// Built-ins, treated as members of the module `builtins`
base = MkModuleImport("builtins") and
lbl = Label::member(any(string name | ref = Builtins::likelyBuiltin(name)))
or
// Unknown variables that may belong to a module imported with `import *`
exists(Scope s |
exists(py::Scope s |
base = potential_import_star_base(s) and
lbl =
Label::member(any(string name |
@@ -444,7 +618,7 @@ module API {
)
or
// Ensure the Python 2 `__builtin__` module gets the name of the Python 3 `builtins` module.
major_version() = 2 and
py::major_version() = 2 and
nd = MkModuleImport("builtins") and
imports(ref, "__builtin__")
or
@@ -466,6 +640,42 @@ module API {
exists(DataFlow::TypeTracker t2 | result = trackUseNode(src, t2).track(t2, t))
}
/**
* Holds if `arg` is passed as an argument to a use of `base`.
*
* `lbl` is represents which parameter of the function was passed. Either a numbered parameter, or a keyword parameter.
*/
private predicate argumentPassing(TApiNode base, Label::ApiLabel lbl, DataFlow::Node arg) {
exists(DataFlow::Node use, DataFlow::LocalSourceNode pred |
use(base, use) and pred = trackUseNode(use)
|
exists(int i |
lbl = Label::parameter(i) and
arg = pred.getACall().getArg(i)
)
or
exists(string name | lbl = Label::keywordParameter(name) |
arg = pred.getACall().getArgByName(name)
)
)
}
/**
* Gets a node that inter-procedurally flows into `nd`, which is a definition of some node.
*/
cached
DataFlow::LocalSourceNode trackDefNode(DataFlow::Node nd) {
result = trackDefNode(nd, DataFlow::TypeBackTracker::end())
}
private DataFlow::LocalSourceNode trackDefNode(DataFlow::Node nd, DataFlow::TypeBackTracker t) {
t.start() and
rhs(_, nd) and
result = nd.getALocalSource()
or
exists(DataFlow::TypeBackTracker t2 | result = trackDefNode(nd, t2).backtrack(t2, t))
}
/**
* Gets a data-flow node to which `src`, which is a use of an API-graph node, flows.
*
@@ -477,6 +687,12 @@ module API {
not result instanceof DataFlow::ModuleVariableNode
}
/**
* Holds if `rhs` is the right-hand side of a definition of node `nd`.
*/
cached
predicate rhs(TApiNode nd, DataFlow::Node rhs) { nd = MkDef(rhs) }
/**
* Holds if there is an edge from `pred` to `succ` in the API graph that is labeled with `lbl`.
*/
@@ -485,8 +701,7 @@ module API {
/* There's an edge from the root node for each imported module. */
exists(string m |
pred = MkRoot() and
lbl = Label::mod(m)
|
lbl = Label::mod(m) and
succ = MkModuleImport(m) and
// Only allow undotted names to count as base modules.
not m.matches("%.%")
@@ -503,6 +718,11 @@ module API {
use(pred, lbl, ref) and
succ = MkUse(ref)
)
or
exists(DataFlow::Node rhs |
rhs(pred, lbl, rhs) and
succ = MkDef(rhs)
)
}
/**
@@ -530,16 +750,27 @@ module API {
private import semmle.python.dataflow.new.internal.ImportStar
newtype TLabel =
MkLabelModule(string mod) { exists(Impl::MkModuleImport(mod)) } or
MkLabelModule(string mod) {
exists(Impl::MkModuleImport(mod)) and
not mod.matches("%.%") // only top level modules count as base modules
} or
MkLabelMember(string member) {
member = any(DataFlow::AttrRef pr).getAttributeName() or
exists(Builtins::likelyBuiltin(member)) or
ImportStar::namePossiblyDefinedInImportStar(_, member, _) or
Impl::prefix_member(_, member, _)
Impl::prefix_member(_, member, _) or
member = any(py::Dict d).getAnItem().(py::KeyValuePair).getKey().(py::StrConst).getS()
} or
MkLabelUnknownMember() or
MkLabelParameter(int i) {
none() // TODO: Fill in when adding def nodes
exists(any(DataFlow::CallCfgNode c).getArg(i))
or
exists(any(py::Function f).getArg(i))
} or
MkLabelKeywordParameter(string name) {
exists(any(DataFlow::CallCfgNode c).getArgByName(name))
or
exists(any(py::Function f).getArgByName(name))
} or
MkLabelReturn() or
MkLabelSubclass() or
@@ -588,6 +819,18 @@ module API {
int getIndex() { result = i }
}
/** A label for a keyword parameter `name`. */
class LabelKeywordParameter extends ApiLabel {
string name;
LabelKeywordParameter() { this = MkLabelKeywordParameter(name) }
override string toString() { result = "getKeywordParameter(\"" + name + "\")" }
/** Gets the name of the parameter for this label. */
string getName() { result = name }
}
/** A label that gets the return value of a function. */
class LabelReturn extends ApiLabel {
LabelReturn() { this = MkLabelReturn() }
@@ -620,13 +863,19 @@ module API {
LabelUnknownMember unknownMember() { any() }
/** Gets the `member` edge label for the given attribute reference. */
ApiLabel memberFromRef(DataFlow::AttrRef pr) {
result = member(pr.getAttributeName())
ApiLabel memberFromRef(DataFlow::AttrRef ref) {
result = member(ref.getAttributeName())
or
not exists(pr.getAttributeName()) and
not exists(ref.getAttributeName()) and
result = unknownMember()
}
/** Gets the `parameter` edge label for parameter `i`. */
LabelParameter parameter(int i) { result.getIndex() = i }
/** Gets the `parameter` edge label for the keyword parameter `name`. */
LabelKeywordParameter keywordParameter(string name) { result.getName() = name }
/** Gets the `return` edge label. */
LabelReturn return() { any() }

View File

@@ -552,7 +552,7 @@ class RegExpWordBoundary extends RegExpSpecialChar {
/**
* A character class escape in a regular expression.
* That is, an escaped charachter that denotes multiple characters.
* That is, an escaped character that denotes multiple characters.
*
* Examples:
*

View File

@@ -50,26 +50,10 @@ private module Aiomysql {
* A query. Calling `execute` on a `Cursor` constructs a query.
* See https://aiomysql.readthedocs.io/en/stable/cursors.html#Cursor.execute
*/
class CursorExecuteCall extends SqlConstruction::Range, DataFlow::CallCfgNode {
class CursorExecuteCall extends SqlConstruction::Range, API::CallNode {
CursorExecuteCall() { this = cursor().getMember("execute").getACall() }
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("operation")] }
}
/**
* This is only needed to connect the argument to the execute call with the subsequnt awaiting.
* It should be obsolete once we have `API::CallNode` available.
*/
private DataFlow::TypeTrackingNode cursorExecuteCall(DataFlow::TypeTracker t, DataFlow::Node sql) {
// cursor created from connection
t.start() and
sql = result.(CursorExecuteCall).getSql()
or
exists(DataFlow::TypeTracker t2 | result = cursorExecuteCall(t2, sql).track(t2, t))
}
DataFlow::Node cursorExecuteCall(DataFlow::Node sql) {
cursorExecuteCall(DataFlow::TypeTracker::end(), sql).flowsTo(result)
override DataFlow::Node getSql() { result = this.getParameter(0, "operation").getARhs() }
}
/**
@@ -77,11 +61,11 @@ private module Aiomysql {
* See https://aiomysql.readthedocs.io/en/stable/cursors.html#Cursor.execute
*/
class AwaitedCursorExecuteCall extends SqlExecution::Range {
DataFlow::Node sql;
CursorExecuteCall executeCall;
AwaitedCursorExecuteCall() { this = awaited(cursorExecuteCall(sql)) }
AwaitedCursorExecuteCall() { this = executeCall.getReturn().getAwaited().getAnImmediateUse() }
override DataFlow::Node getSql() { result = sql }
override DataFlow::Node getSql() { result = executeCall.getSql() }
}
/**
@@ -107,28 +91,10 @@ private module Aiomysql {
* A query. Calling `execute` on a `SAConnection` constructs a query.
* See https://aiomysql.readthedocs.io/en/stable/sa.html#aiomysql.sa.SAConnection.execute
*/
class SAConnectionExecuteCall extends SqlConstruction::Range, DataFlow::CallCfgNode {
class SAConnectionExecuteCall extends SqlConstruction::Range, API::CallNode {
SAConnectionExecuteCall() { this = saConnection().getMember("execute").getACall() }
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("query")] }
}
/**
* This is only needed to connect the argument to the execute call with the subsequnt awaiting.
* It should be obsolete once we have `API::CallNode` available.
*/
private DataFlow::TypeTrackingNode saConnectionExecuteCall(
DataFlow::TypeTracker t, DataFlow::Node sql
) {
// saConnection created from engine
t.start() and
sql = result.(SAConnectionExecuteCall).getSql()
or
exists(DataFlow::TypeTracker t2 | result = saConnectionExecuteCall(t2, sql).track(t2, t))
}
DataFlow::Node saConnectionExecuteCall(DataFlow::Node sql) {
saConnectionExecuteCall(DataFlow::TypeTracker::end(), sql).flowsTo(result)
override DataFlow::Node getSql() { result = this.getParameter(0, "query").getARhs() }
}
/**
@@ -136,10 +102,10 @@ private module Aiomysql {
* See https://aiomysql.readthedocs.io/en/stable/sa.html#aiomysql.sa.SAConnection.execute
*/
class AwaitedSAConnectionExecuteCall extends SqlExecution::Range {
DataFlow::Node sql;
SAConnectionExecuteCall execute;
AwaitedSAConnectionExecuteCall() { this = awaited(saConnectionExecuteCall(sql)) }
AwaitedSAConnectionExecuteCall() { this = execute.getReturn().getAwaited().getAnImmediateUse() }
override DataFlow::Node getSql() { result = sql }
override DataFlow::Node getSql() { result = execute.getSql() }
}
}

View File

@@ -50,26 +50,10 @@ private module Aiopg {
* A query. Calling `execute` on a `Cursor` constructs a query.
* See https://aiopg.readthedocs.io/en/stable/core.html#aiopg.Cursor.execute
*/
class CursorExecuteCall extends SqlConstruction::Range, DataFlow::CallCfgNode {
class CursorExecuteCall extends SqlConstruction::Range, API::CallNode {
CursorExecuteCall() { this = cursor().getMember("execute").getACall() }
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("operation")] }
}
/**
* This is only needed to connect the argument to the execute call with the subsequnt awaiting.
* It should be obsolete once we have `API::CallNode` available.
*/
private DataFlow::TypeTrackingNode cursorExecuteCall(DataFlow::TypeTracker t, DataFlow::Node sql) {
// cursor created from connection
t.start() and
sql = result.(CursorExecuteCall).getSql()
or
exists(DataFlow::TypeTracker t2 | result = cursorExecuteCall(t2, sql).track(t2, t))
}
DataFlow::Node cursorExecuteCall(DataFlow::Node sql) {
cursorExecuteCall(DataFlow::TypeTracker::end(), sql).flowsTo(result)
override DataFlow::Node getSql() { result = this.getParameter(0, "operation").getARhs() }
}
/**
@@ -77,11 +61,11 @@ private module Aiopg {
* See https://aiopg.readthedocs.io/en/stable/core.html#aiopg.Cursor.execute
*/
class AwaitedCursorExecuteCall extends SqlExecution::Range {
DataFlow::Node sql;
CursorExecuteCall execute;
AwaitedCursorExecuteCall() { this = awaited(cursorExecuteCall(sql)) }
AwaitedCursorExecuteCall() { this = execute.getReturn().getAwaited().getAnImmediateUse() }
override DataFlow::Node getSql() { result = sql }
override DataFlow::Node getSql() { result = execute.getSql() }
}
/**
@@ -103,28 +87,10 @@ private module Aiopg {
* A query. Calling `execute` on a `SAConnection` constructs a query.
* See https://aiopg.readthedocs.io/en/stable/sa.html#aiopg.sa.SAConnection.execute
*/
class SAConnectionExecuteCall extends SqlConstruction::Range, DataFlow::CallCfgNode {
class SAConnectionExecuteCall extends SqlConstruction::Range, API::CallNode {
SAConnectionExecuteCall() { this = saConnection().getMember("execute").getACall() }
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("query")] }
}
/**
* This is only needed to connect the argument to the execute call with the subsequnt awaiting.
* It should be obsolete once we have `API::CallNode` available.
*/
private DataFlow::TypeTrackingNode saConnectionExecuteCall(
DataFlow::TypeTracker t, DataFlow::Node sql
) {
// saConnection created from engine
t.start() and
sql = result.(SAConnectionExecuteCall).getSql()
or
exists(DataFlow::TypeTracker t2 | result = saConnectionExecuteCall(t2, sql).track(t2, t))
}
DataFlow::Node saConnectionExecuteCall(DataFlow::Node sql) {
saConnectionExecuteCall(DataFlow::TypeTracker::end(), sql).flowsTo(result)
override DataFlow::Node getSql() { result = this.getParameter(0, "query").getARhs() }
}
/**
@@ -132,10 +98,10 @@ private module Aiopg {
* See https://aiopg.readthedocs.io/en/stable/sa.html#aiopg.sa.SAConnection.execute
*/
class AwaitedSAConnectionExecuteCall extends SqlExecution::Range {
DataFlow::Node sql;
SAConnectionExecuteCall excute;
AwaitedSAConnectionExecuteCall() { this = awaited(saConnectionExecuteCall(sql)) }
AwaitedSAConnectionExecuteCall() { this = excute.getReturn().getAwaited().getAnImmediateUse() }
override DataFlow::Node getSql() { result = sql }
override DataFlow::Node getSql() { result = excute.getSql() }
}
}

View File

@@ -71,48 +71,27 @@ private module Asyncpg {
* The result of calling `prepare(query)` is a `PreparedStatementFactory` and the argument, `query` needs to
* be tracked to the place where a `PreparedStatement` is created and then futher to any executing methods.
* Hence the two type trackers.
*
* TODO: Rewrite this, once we have `API::CallNode` available.
*/
module PreparedStatement {
class PreparedStatementConstruction extends SqlConstruction::Range, DataFlow::CallCfgNode {
class PreparedStatementConstruction extends SqlConstruction::Range, API::CallNode {
PreparedStatementConstruction() { this = connection().getMember("prepare").getACall() }
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("query")] }
override DataFlow::Node getSql() { result = this.getParameter(0, "query").getARhs() }
}
private DataFlow::TypeTrackingNode preparedStatementFactory(
DataFlow::TypeTracker t, DataFlow::Node sql
) {
t.start() and
sql = result.(PreparedStatementConstruction).getSql()
or
exists(DataFlow::TypeTracker t2 | result = preparedStatementFactory(t2, sql).track(t2, t))
}
DataFlow::Node preparedStatementFactory(DataFlow::Node sql) {
preparedStatementFactory(DataFlow::TypeTracker::end(), sql).flowsTo(result)
}
private DataFlow::TypeTrackingNode preparedStatement(DataFlow::TypeTracker t, DataFlow::Node sql) {
t.start() and
result = awaited(preparedStatementFactory(sql))
or
exists(DataFlow::TypeTracker t2 | result = preparedStatement(t2, sql).track(t2, t))
}
DataFlow::Node preparedStatement(DataFlow::Node sql) {
preparedStatement(DataFlow::TypeTracker::end(), sql).flowsTo(result)
}
class PreparedStatementExecution extends SqlExecution::Range, DataFlow::MethodCallNode {
DataFlow::Node sql;
class PreparedStatementExecution extends SqlExecution::Range, API::CallNode {
PreparedStatementConstruction prepareCall;
PreparedStatementExecution() {
this.calls(preparedStatement(sql), ["executemany", "fetch", "fetchrow", "fetchval"])
this =
prepareCall
.getReturn()
.getAwaited()
.getMember(["executemany", "fetch", "fetchrow", "fetchval"])
.getACall()
}
override DataFlow::Node getSql() { result = sql }
override DataFlow::Node getSql() { result = prepareCall.getSql() }
}
}
@@ -124,37 +103,36 @@ private module Asyncpg {
* The result of calling `cursor` in either case is a `CursorFactory` and the argument, `query` needs to
* be tracked to the place where a `Cursor` is created, hence the type tracker.
* The creation of the `Cursor` executes the query.
*
* TODO: Rewrite this, once we have `API::CallNode` available.
*/
module Cursor {
class CursorConstruction extends SqlConstruction::Range, DataFlow::CallCfgNode {
class CursorConstruction extends SqlConstruction::Range, API::CallNode {
CursorConstruction() { this = connection().getMember("cursor").getACall() }
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("query")] }
}
private DataFlow::TypeTrackingNode cursorFactory(DataFlow::TypeTracker t, DataFlow::Node sql) {
// cursor created from connection
t.start() and
sql = result.(CursorConstruction).getSql()
or
// cursor created from prepared statement
t.start() and
result.(DataFlow::MethodCallNode).calls(PreparedStatement::preparedStatement(sql), "cursor")
or
exists(DataFlow::TypeTracker t2 | result = cursorFactory(t2, sql).track(t2, t))
}
DataFlow::Node cursorFactory(DataFlow::Node sql) {
cursorFactory(DataFlow::TypeTracker::end(), sql).flowsTo(result)
override DataFlow::Node getSql() { result = this.getParameter(0, "query").getARhs() }
}
/** The creation of a `Cursor` executes the associated query. */
class CursorCreation extends SqlExecution::Range {
DataFlow::Node sql;
CursorCreation() { this = awaited(cursorFactory(sql)) }
CursorCreation() {
exists(CursorConstruction c |
sql = c.getSql() and
this = c.getReturn().getAwaited().getAnImmediateUse()
)
or
exists(PreparedStatement::PreparedStatementConstruction prepareCall |
sql = prepareCall.getSql() and
this =
prepareCall
.getReturn()
.getAwaited()
.getMember("cursor")
.getReturn()
.getAwaited()
.getAnImmediateUse()
)
}
override DataFlow::Node getSql() { result = sql }
}

View File

@@ -24,7 +24,7 @@ private import semmle.python.frameworks.Stdlib
* - https://docs.python-requests.org/en/latest/
*/
private module Requests {
private class OutgoingRequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode {
private class OutgoingRequestCall extends HTTP::Client::Request::Range, API::CallNode {
string methodName;
OutgoingRequestCall() {
@@ -54,14 +54,11 @@ private module Requests {
result = this.getArg(1)
}
/** Gets the `verify` argument to this outgoing requests call. */
DataFlow::Node getVerifyArg() { result = this.getArgByName("verify") }
override predicate disablesCertificateValidation(
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
) {
disablingNode = this.getVerifyArg() and
argumentOrigin = verifyArgBacktracker(disablingNode) and
disablingNode = this.getKeywordParameter("verify").getARhs() and
argumentOrigin = this.getKeywordParameter("verify").getAValueReachingRhs() and
argumentOrigin.asExpr().(ImmutableLiteral).booleanValue() = false and
not argumentOrigin.asExpr() instanceof None
}
@@ -79,22 +76,6 @@ private module Requests {
}
}
/** Gets a back-reference to the verify argument `arg`. */
private DataFlow::TypeTrackingNode verifyArgBacktracker(
DataFlow::TypeBackTracker t, DataFlow::Node arg
) {
t.start() and
arg = any(OutgoingRequestCall c).getVerifyArg() and
result = arg.getALocalSource()
or
exists(DataFlow::TypeBackTracker t2 | result = verifyArgBacktracker(t2, arg).backtrack(t2, t))
}
/** Gets a back-reference to the verify argument `arg`. */
private DataFlow::LocalSourceNode verifyArgBacktracker(DataFlow::Node arg) {
result = verifyArgBacktracker(DataFlow::TypeBackTracker::end(), arg)
}
// ---------------------------------------------------------------------------
// Response
// ---------------------------------------------------------------------------

View File

@@ -2595,81 +2595,37 @@ private module StdlibPrivate {
// ---------------------------------------------------------------------------
// hashlib
// ---------------------------------------------------------------------------
/** Gets a back-reference to the hashname argument `arg` that was used in a call to `hashlib.new`. */
private DataFlow::TypeTrackingNode hashlibNewCallNameBacktracker(
DataFlow::TypeBackTracker t, DataFlow::Node arg
) {
t.start() and
hashlibNewCallImpl(_, arg) and
result = arg.getALocalSource()
or
exists(DataFlow::TypeBackTracker t2 |
result = hashlibNewCallNameBacktracker(t2, arg).backtrack(t2, t)
)
}
/** Gets a back-reference to the hashname argument `arg` that was used in a call to `hashlib.new`. */
private DataFlow::LocalSourceNode hashlibNewCallNameBacktracker(DataFlow::Node arg) {
result = hashlibNewCallNameBacktracker(DataFlow::TypeBackTracker::end(), arg)
}
/** Holds when `call` is a call to `hashlib.new` with `nameArg` as the first argument. */
private predicate hashlibNewCallImpl(DataFlow::CallCfgNode call, DataFlow::Node nameArg) {
call = API::moduleImport("hashlib").getMember("new").getACall() and
nameArg in [call.getArg(0), call.getArgByName("name")]
}
/** Gets a call to `hashlib.new` with `algorithmName` as the first argument. */
private DataFlow::CallCfgNode hashlibNewCall(string algorithmName) {
exists(DataFlow::Node origin, DataFlow::Node nameArg |
origin = hashlibNewCallNameBacktracker(nameArg) and
algorithmName = origin.asExpr().(StrConst).getText() and
hashlibNewCallImpl(result, nameArg)
)
}
/** Gets a reference to the result of calling `hashlib.new` with `algorithmName` as the first argument. */
private DataFlow::TypeTrackingNode hashlibNewResult(DataFlow::TypeTracker t, string algorithmName) {
t.start() and
result = hashlibNewCall(algorithmName)
or
exists(DataFlow::TypeTracker t2 | result = hashlibNewResult(t2, algorithmName).track(t2, t))
}
/** Gets a reference to the result of calling `hashlib.new` with `algorithmName` as the first argument. */
DataFlow::Node hashlibNewResult(string algorithmName) {
hashlibNewResult(DataFlow::TypeTracker::end(), algorithmName).flowsTo(result)
private API::CallNode hashlibNewCall(string algorithmName) {
algorithmName =
result.getParameter(0, "name").getAValueReachingRhs().asExpr().(StrConst).getText() and
result = API::moduleImport("hashlib").getMember("new").getACall()
}
/**
* A hashing operation by supplying initial data when calling the `hashlib.new` function.
*/
class HashlibNewCall extends Cryptography::CryptographicOperation::Range, DataFlow::CallCfgNode {
class HashlibNewCall extends Cryptography::CryptographicOperation::Range, API::CallNode {
string hashName;
HashlibNewCall() {
this = hashlibNewCall(hashName) and
exists([this.getArg(1), this.getArgByName("data")])
exists(this.getParameter(1, "data"))
}
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(hashName) }
override DataFlow::Node getAnInput() { result in [this.getArg(1), this.getArgByName("data")] }
override DataFlow::Node getAnInput() { result = this.getParameter(1, "data").getARhs() }
}
/**
* A hashing operation by using the `update` method on the result of calling the `hashlib.new` function.
*/
class HashlibNewUpdateCall extends Cryptography::CryptographicOperation::Range,
DataFlow::CallCfgNode {
class HashlibNewUpdateCall extends Cryptography::CryptographicOperation::Range, API::CallNode {
string hashName;
HashlibNewUpdateCall() {
exists(DataFlow::AttrRead attr |
attr.getObject() = hashlibNewResult(hashName) and
this.getFunction() = attr and
attr.getAttributeName() = "update"
)
this = hashlibNewCall(hashName).getReturn().getMember("update").getACall()
}
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(hashName) }

View File

@@ -1,6 +1,8 @@
import python
deprecated import semmle.python.objects.ObjectInternal as OI
private import semmle.python.ApiGraphs
// Need to import since frameworks can extend the abstract `RegexString`
private import semmle.python.Frameworks
/**
* Gets the positional argument index containing the regular expression flags for the member of the
@@ -186,7 +188,7 @@ abstract class RegexString extends Expr {
)
}
/** Hold is a character set starts between `start` and `end`. */
/** Holds if a character set starts between `start` and `end`. */
predicate char_set_start(int start, int end) {
this.char_set_start(start) = true and
(
@@ -314,8 +316,10 @@ abstract class RegexString extends Expr {
result = this.(Bytes).getS()
}
/** Gets the `i`th character of this regex */
string getChar(int i) { result = this.getText().charAt(i) }
/** Gets the `i`th character of this regex, unless it is part of a character escape sequence. */
string nonEscapedCharAt(int i) {
result = this.getText().charAt(i) and
not exists(int x, int y | this.escapedCharacter(x, y) and i in [x .. y - 1])
@@ -327,6 +331,9 @@ abstract class RegexString extends Expr {
private predicate isGroupStart(int i) { this.nonEscapedCharAt(i) = "(" and not this.inCharSet(i) }
/**
* Holds if the `i`th character could not be parsed.
*/
predicate failedToParse(int i) {
exists(this.getChar(i)) and
not exists(int start, int end |
@@ -415,6 +422,9 @@ abstract class RegexString extends Expr {
)
}
/**
* Holds if a simple or escaped character is found between `start` and `end`.
*/
predicate character(int start, int end) {
(
this.simpleCharacter(start, end) and
@@ -426,12 +436,18 @@ abstract class RegexString extends Expr {
not exists(int x, int y | this.backreference(x, y) and x <= start and y >= end)
}
/**
* Holds if a normal character is found between `start` and `end`.
*/
predicate normalCharacter(int start, int end) {
end = start + 1 and
this.character(start, end) and
not this.specialCharacter(start, end, _)
}
/**
* Holds if a special character is found between `start` and `end`.
*/
predicate specialCharacter(int start, int end, string char) {
not this.inCharSet(start) and
this.character(start, end) and
@@ -490,7 +506,7 @@ abstract class RegexString extends Expr {
this.specialCharacter(start, end, _)
}
/** Whether the text in the range start,end is a group */
/** Whether the text in the range `start,end` is a group */
predicate group(int start, int end) {
this.groupContents(start, end, _, _)
or
@@ -609,6 +625,7 @@ abstract class RegexString extends Expr {
this.simple_group_start(start, end)
}
/** Matches the start of a non-capturing group, e.g. `(?:` */
private predicate non_capturing_group_start(int start, int end) {
this.isGroupStart(start) and
this.getChar(start + 1) = "?" and
@@ -616,12 +633,18 @@ abstract class RegexString extends Expr {
end = start + 3
}
/** Matches the start of a simple group, e.g. `(a+)`. */
private predicate simple_group_start(int start, int end) {
this.isGroupStart(start) and
this.getChar(start + 1) != "?" and
end = start + 1
}
/**
* Matches the start of a named group, such as:
* - `(?<name>\w+)`
* - `(?'name'\w+)`
*/
private predicate named_group_start(int start, int end) {
this.isGroupStart(start) and
this.getChar(start + 1) = "?" and
@@ -673,6 +696,7 @@ abstract class RegexString extends Expr {
)
}
/** Matches the start of a positive lookahead assertion, i.e. `(?=`. */
private predicate lookahead_assertion_start(int start, int end) {
this.isGroupStart(start) and
this.getChar(start + 1) = "?" and
@@ -680,6 +704,7 @@ abstract class RegexString extends Expr {
end = start + 3
}
/** Matches the start of a negative lookahead assertion, i.e. `(?!`. */
private predicate negative_lookahead_assertion_start(int start, int end) {
this.isGroupStart(start) and
this.getChar(start + 1) = "?" and
@@ -687,6 +712,7 @@ abstract class RegexString extends Expr {
end = start + 3
}
/** Matches the start of a positive lookbehind assertion, i.e. `(?<=`. */
private predicate lookbehind_assertion_start(int start, int end) {
this.isGroupStart(start) and
this.getChar(start + 1) = "?" and
@@ -695,6 +721,7 @@ abstract class RegexString extends Expr {
end = start + 4
}
/** Matches the start of a negative lookbehind assertion, i.e. `(?<!`. */
private predicate negative_lookbehind_assertion_start(int start, int end) {
this.isGroupStart(start) and
this.getChar(start + 1) = "?" and
@@ -703,6 +730,7 @@ abstract class RegexString extends Expr {
end = start + 4
}
/** Matches the start of a comment group, i.e. `(?#`. */
private predicate comment_group_start(int start, int end) {
this.isGroupStart(start) and
this.getChar(start + 1) = "?" and
@@ -710,6 +738,7 @@ abstract class RegexString extends Expr {
end = start + 3
}
/** Matches the contents of a group. */
predicate groupContents(int start, int end, int in_start, int in_end) {
this.group_start(start, in_start) and
end = in_end + 1 and
@@ -717,12 +746,14 @@ abstract class RegexString extends Expr {
this.isGroupEnd(in_end)
}
/** Matches a named backreference, e.g. `\k<foo>`. */
private predicate named_backreference(int start, int end, string name) {
this.named_backreference_start(start, start + 4) and
end = min(int i | i > start + 4 and this.getChar(i) = ")") + 1 and
name = this.getText().substring(start + 4, end - 2)
}
/** Matches a numbered backreference, e.g. `\1`. */
private predicate numbered_backreference(int start, int end, int value) {
this.escapingChar(start) and
// starting with 0 makes it an octal escape
@@ -747,7 +778,7 @@ abstract class RegexString extends Expr {
)
}
/** Whether the text in the range start,end is a back reference */
/** Whether the text in the range `start,end` is a back reference */
predicate backreference(int start, int end) {
this.numbered_backreference(start, end, _)
or

View File

@@ -15,41 +15,11 @@ private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
/**
* Gets a call to a method that makes an outgoing request using the `requests` module,
* such as `requests.get` or `requests.put`, with the specified HTTP verb `verb`
*/
DataFlow::CallCfgNode outgoingRequestCall(string verb) {
verb = HTTP::httpVerbLower() and
result = API::moduleImport("requests").getMember(verb).getACall()
}
/** Gets the "verfiy" argument to a outgoingRequestCall. */
DataFlow::Node verifyArg(DataFlow::CallCfgNode call) {
call = outgoingRequestCall(_) and
result = call.getArgByName("verify")
}
/** Gets a back-reference to the verify argument `arg`. */
private DataFlow::TypeTrackingNode verifyArgBacktracker(
DataFlow::TypeBackTracker t, DataFlow::Node arg
) {
t.start() and
arg = verifyArg(_) and
result = arg.getALocalSource()
or
exists(DataFlow::TypeBackTracker t2 | result = verifyArgBacktracker(t2, arg).backtrack(t2, t))
}
/** Gets a back-reference to the verify argument `arg`. */
DataFlow::LocalSourceNode verifyArgBacktracker(DataFlow::Node arg) {
result = verifyArgBacktracker(DataFlow::TypeBackTracker::end(), arg)
}
from DataFlow::CallCfgNode call, DataFlow::Node falseyOrigin, string verb
from API::CallNode call, DataFlow::Node falseyOrigin, string verb
where
call = outgoingRequestCall(verb) and
falseyOrigin = verifyArgBacktracker(verifyArg(call)) and
verb = HTTP::httpVerbLower() and
call = API::moduleImport("requests").getMember(verb).getACall() and
falseyOrigin = call.getKeywordParameter("verify").getAValueReachingRhs() and
// requests treats `None` as the default and all other "falsey" values as `False`.
falseyOrigin.asExpr().(ImmutableLiteral).booleanValue() = false and
not falseyOrigin.asExpr() instanceof None

View File

@@ -0,0 +1,16 @@
/**
* @name Call graph
* @description An edge in the points-to call graph.
* @kind problem
* @problem.severity recommendation
* @id py/meta/points-to-call-graph
* @tags meta
* @precision very-low
*/
import python
import semmle.python.dataflow.new.internal.DataFlowPrivate
from DataFlowCall c, DataFlowCallableValue f
where c.getCallable() = f
select c, "Call to $@", f.getScope(), f.toString()

View File

@@ -0,0 +1,132 @@
/**
* A test query that verifies assertions about the API graph embedded in source-code comments.
*
* An assertion is a comment of the form `def=<path>` or `use=<path>`, and asserts that
* there is a def/use feature reachable from the root along the given path, and its
* associated data-flow node must start on the same line as the comment.
*
* The query only produces output for failed assertions, meaning that it should have no output
* under normal circumstances.
*
* The syntax is made to look exactly like inline expectation tests, so that the tests
* can remain consistent with other Python tests.
*/
import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.ApiGraphs
private DataFlow::Node getNode(API::Node nd, string kind) {
kind = "def" and
result = nd.getARhs()
or
kind = "use" and
result = nd.getAUse()
}
private string getLocStr(Location loc) {
exists(string filepath, int startline |
loc.hasLocationInfo(filepath, startline, _, _, _) and
result = filepath + ":" + startline
)
}
/**
* An assertion matching a data-flow node against an API-graph feature.
*/
class Assertion extends Comment {
string expectedKind;
string expectedLoc;
string path;
string polarity;
Assertion() {
exists(string txt, string rex |
txt = this.getText().trim() and
rex = "#\\$.*?((?:MISSING: )?)(def|use)=([\\w\\(\\)\"\\.]*).*"
|
polarity = txt.regexpCapture(rex, 1) and
expectedKind = txt.regexpCapture(rex, 2) and
path = txt.regexpCapture(rex, 3) and
expectedLoc = getLocStr(this.getLocation())
)
}
string getEdgeLabel(int i) {
// matches a single edge. E.g. `getParameter(1)` or `getMember("foo")`.
// The lookbehind/lookahead ensure that the boundary is correct, that is
// either the edge is next to a ".", or it's the end of the string.
result = path.regexpFind("(?<=\\.|^)([\\w\\(\\)\"]+)(?=\\.|$)", i, _).trim()
}
int getPathLength() { result = max(int i | exists(this.getEdgeLabel(i))) + 1 }
predicate isNegative() { polarity = "MISSING: " }
API::Node lookup(int i) {
i = 0 and
result = API::root()
or
result =
this.lookup(i - 1)
.getASuccessor(any(API::Label::ApiLabel label |
label.toString() = this.getEdgeLabel(i - 1)
))
}
API::Node lookup() { result = this.lookup(this.getPathLength()) }
predicate holds() { getLocStr(getNode(this.lookup(), expectedKind).getLocation()) = expectedLoc }
string tryExplainFailure() {
exists(int i, API::Node nd, string prefix, string suffix |
nd = this.lookup(i) and
i < getPathLength() and
not exists(this.lookup([i + 1 .. getPathLength()])) and
prefix = nd + " has no outgoing edge labelled " + this.getEdgeLabel(i) + ";" and
if exists(nd.getASuccessor())
then
suffix =
"it does have outgoing edges labelled " +
concat(string lbl |
exists(nd.getASuccessor(any(API::Label::ApiLabel label | label.toString() = lbl)))
|
lbl, ", "
) + "."
else suffix = "it has no outgoing edges at all."
|
result = prefix + " " + suffix
)
or
exists(API::Node nd, string kind | nd = this.lookup() |
exists(getNode(nd, kind)) and
not exists(getNode(nd, expectedKind)) and
result = "Expected " + expectedKind + " node, but found " + kind + " node."
)
or
exists(DataFlow::Node nd | nd = getNode(this.lookup(), expectedKind) |
not getLocStr(nd.getLocation()) = expectedLoc and
result =
"Node not found on this line (but there is one on line " + min(getLocStr(nd.getLocation())) +
")."
)
}
string explainFailure() {
if this.isNegative()
then (
this.holds() and
result = "Negative assertion failed."
) else (
not this.holds() and
(
result = this.tryExplainFailure()
or
not exists(this.tryExplainFailure()) and
result = "Positive assertion failed for unknown reasons."
)
)
}
}
query predicate failed(Assertion a, string explanation) { explanation = a.explainFailure() }

View File

@@ -1,26 +0,0 @@
import python
import semmle.python.dataflow.new.DataFlow
import TestUtilities.InlineExpectationsTest
import semmle.python.ApiGraphs
class AwaitedTest extends InlineExpectationsTest {
AwaitedTest() { this = "AwaitedTest" }
override string getARelevantTag() { result = "awaited" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(API::Node awaited, DataFlow::Node use, API::Node pred |
awaited = pred.getAwaited() and
use = awaited.getAUse() and
location = use.getLocation() and
// Module variable nodes have no suitable location, so it's best to simply exclude them entirely
// from the inline tests.
not use instanceof DataFlow::ModuleVariableNode and
exists(location.getFile().getRelativePath())
|
tag = "awaited" and
value = pred.getPath() and
element = use.toString()
)
}
}

View File

@@ -1,35 +0,0 @@
import python
import semmle.python.dataflow.new.DataFlow
import TestUtilities.InlineExpectationsTest
import semmle.python.ApiGraphs
class ApiUseTest extends InlineExpectationsTest {
ApiUseTest() { this = "ApiUseTest" }
override string getARelevantTag() { result = "use" }
private predicate relevant_node(API::Node a, DataFlow::Node n, Location l) {
n = a.getAUse() and
l = n.getLocation() and
// Module variable nodes have no suitable location, so it's best to simply exclude them entirely
// from the inline tests.
not n instanceof DataFlow::ModuleVariableNode and
exists(l.getFile().getRelativePath())
}
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(API::Node a, DataFlow::Node n | relevant_node(a, n, location) |
tag = "use" and
// Only report the longest path on this line:
value =
max(API::Node a2, Location l2 |
relevant_node(a2, _, l2) and
l2.getFile() = location.getFile() and
l2.getStartLine() = location.getStartLine()
|
a2.getPath()
) and
element = n.toString()
)
}
}

View File

@@ -0,0 +1 @@
import TestUtilities.VerifyApiGraphs

View File

@@ -0,0 +1,61 @@
from mypkg import foo #$ use=moduleImport("mypkg").getMember("foo")
def callback(x): #$ use=moduleImport("mypkg").getMember("foo").getMember("bar").getParameter(0).getParameter(0)
x.baz() #$ use=moduleImport("mypkg").getMember("foo").getMember("bar").getParameter(0).getParameter(0).getMember("baz").getReturn()
foo.bar(callback) #$ def=moduleImport("mypkg").getMember("foo").getMember("bar").getParameter(0) use=moduleImport("mypkg").getMember("foo").getMember("bar").getReturn()
def callback2(x): #$ use=moduleImport("mypkg").getMember("foo").getMember("baz").getParameter(0).getMember("c").getParameter(0)
x.baz2() #$ use=moduleImport("mypkg").getMember("foo").getMember("baz").getParameter(0).getMember("c").getParameter(0).getMember("baz2").getReturn()
mydict = {
"c": callback2, #$ def=moduleImport("mypkg").getMember("foo").getMember("baz").getParameter(0).getMember("c")
"other": "whatever" #$ def=moduleImport("mypkg").getMember("foo").getMember("baz").getParameter(0).getMember("other")
}
foo.baz(mydict) #$ def=moduleImport("mypkg").getMember("foo").getMember("baz").getParameter(0) use=moduleImport("mypkg").getMember("foo").getMember("baz").getReturn()
def callback3(x): #$ use=moduleImport("mypkg").getMember("foo").getMember("baz").getParameter(0).getMember("third").getParameter(0)
x.baz3() #$ use=moduleImport("mypkg").getMember("foo").getMember("baz").getParameter(0).getMember("third").getParameter(0).getMember("baz3").getReturn()
mydict.third = callback3 #$ def=moduleImport("mypkg").getMember("foo").getMember("baz").getParameter(0).getMember("third")
foo.blab(mydict) #$ def=moduleImport("mypkg").getMember("foo").getMember("blab").getParameter(0) use=moduleImport("mypkg").getMember("foo").getMember("blab").getReturn()
def callback4(x): #$ use=moduleImport("mypkg").getMember("foo").getMember("quack").getParameter(0).getParameter(0)
x.baz4() #$ use=moduleImport("mypkg").getMember("foo").getMember("quack").getParameter(0).getParameter(0).getMember("baz4").getReturn()
otherDict = {
# TODO: Backtracking through a property set using a dict doesn't work, but I can backtrack through explicit property writes, e.g. the `otherDict.fourth` below.
# TODO: There is a related TODO in ApiGraphs.qll
"blab": "whatever"
}
otherDict.fourth = callback4
foo.quack(otherDict.fourth) #$ def=moduleImport("mypkg").getMember("foo").getMember("quack").getParameter(0) use=moduleImport("mypkg").getMember("foo").getMember("quack").getReturn()
def namedCallback(myName, otherName):
# Using named parameters:
myName() #$ use=moduleImport("mypkg").getMember("foo").getMember("blob").getParameter(0).getKeywordParameter("myName").getReturn()
otherName() #$ use=moduleImport("mypkg").getMember("foo").getMember("blob").getParameter(0).getKeywordParameter("otherName").getReturn()
# Using numbered parameters:
myName() #$ use=moduleImport("mypkg").getMember("foo").getMember("blob").getParameter(0).getParameter(0).getReturn()
otherName() #$ use=moduleImport("mypkg").getMember("foo").getMember("blob").getParameter(0).getParameter(1).getReturn()
foo.blob(namedCallback) #$ use=moduleImport("mypkg").getMember("foo").getMember("blob").getReturn()
foo.named(myName = 2) #$ def=moduleImport("mypkg").getMember("foo").getMember("named").getKeywordParameter("myName")
def recusisionCallback(x):
x.recursion() #$ use=moduleImport("mypkg").getMember("foo").getMember("rec").getParameter(0).getMember("callback").getParameter(0).getMember("recursion").getReturn()
x.recursion() #$ use=moduleImport("mypkg").getMember("foo").getMember("rec").getParameter(0).getMember("rec1").getMember("callback").getParameter(0).getMember("recursion").getReturn()
x.recursion() #$ use=moduleImport("mypkg").getMember("foo").getMember("rec").getParameter(0).getMember("rec1").getMember("rec2").getMember("callback").getParameter(0).getMember("recursion").getReturn()
x.recursion() #$ use=moduleImport("mypkg").getMember("foo").getMember("rec").getParameter(0).getMember("rec1").getMember("rec2").getMember("rec1").getMember("callback").getParameter(0).getMember("recursion").getReturn()
recursiveDict = {};
recursiveDict.callback = recusisionCallback;
recursiveDict.rec1 = recursiveDict;
recursiveDict.rec2 = recursiveDict;
foo.rec(recursiveDict); #$ def=moduleImport("mypkg").getMember("foo").getMember("rec").getParameter(0)

View File

@@ -0,0 +1,19 @@
# Subclasses
from flask.views import View #$ use=moduleImport("flask").getMember("views").getMember("View")
class MyView(View): #$ use=moduleImport("flask").getMember("views").getMember("View").getASubclass()
myvar = 45 #$ def=moduleImport("flask").getMember("views").getMember("View").getASubclass().getMember("myvar")
def my_method(self): #$ def=moduleImport("flask").getMember("views").getMember("View").getASubclass().getMember("my_method")
return 3 #$ def=moduleImport("flask").getMember("views").getMember("View").getASubclass().getMember("my_method").getReturn()
instance = MyView() #$ use=moduleImport("flask").getMember("views").getMember("View").getASubclass().getReturn()
def internal():
from pflask.views import View #$ use=moduleImport("pflask").getMember("views").getMember("View")
class IntMyView(View): #$ use=moduleImport("pflask").getMember("views").getMember("View").getASubclass()
my_internal_var = 35 #$ def=moduleImport("pflask").getMember("views").getMember("View").getASubclass().getMember("my_internal_var")
def my_internal_method(self): #$ def=moduleImport("pflask").getMember("views").getMember("View").getASubclass().getMember("my_internal_method")
pass
int_instance = IntMyView() #$ use=moduleImport("pflask").getMember("views").getMember("View").getASubclass().getReturn()

View File

@@ -75,27 +75,6 @@ def f():
sink(foo) #$ use=moduleImport("danger").getMember("SOURCE")
# Subclasses
from flask.views import View #$ use=moduleImport("flask").getMember("views").getMember("View")
class MyView(View): #$ use=moduleImport("flask").getMember("views").getMember("View").getASubclass()
myvar = 45 #$ use=moduleImport("flask").getMember("views").getMember("View").getASubclass().getMember("myvar")
def my_method(self): #$ use=moduleImport("flask").getMember("views").getMember("View").getASubclass().getMember("my_method")
pass
instance = MyView() #$ use=moduleImport("flask").getMember("views").getMember("View").getASubclass().getReturn()
def internal():
from pflask.views import View #$ use=moduleImport("pflask").getMember("views").getMember("View")
class IntMyView(View): #$ use=moduleImport("pflask").getMember("views").getMember("View").getASubclass()
my_internal_var = 35 #$ use=moduleImport("pflask").getMember("views").getMember("View").getASubclass().getMember("my_internal_var")
def my_internal_method(self): #$ use=moduleImport("pflask").getMember("views").getMember("View").getASubclass().getMember("my_internal_method")
pass
int_instance = IntMyView() #$ use=moduleImport("pflask").getMember("views").getMember("View").getASubclass().getReturn()
# Built-ins
def use_of_builtins():

View File

@@ -0,0 +1 @@
import TestUtilities.VerifyApiGraphs

View File

@@ -135,6 +135,8 @@ class TopLevel extends TTopLevel, AstNode {
pred = directMember("getAModule") and result = this.getAModule()
or
pred = directMember("getANewType") and result = this.getANewType()
or
pred = directMember("getQLDoc") and result = this.getQLDoc()
}
QLDoc getQLDocFor(ModuleMember m) {
@@ -1127,6 +1129,21 @@ class Import extends TImport, ModuleMember, ModuleRef {
result = imp.getChild(0).(QL::ImportModuleExpr).getChild().getName(i).getValue()
}
/**
* Gets the full string specifying the module being imported.
*/
string getImportString() {
exists(string selec |
not exists(getSelectionName(_)) and selec = ""
or
selec =
"::" + strictconcat(int i, string q | q = this.getSelectionName(i) | q, "::" order by i)
|
result =
strictconcat(int i, string q | q = this.getQualifiedName(i) | q, "." order by i) + selec
)
}
final override FileOrModule getResolvedModule() { resolve(this, result) }
}

View File

@@ -1,4 +1,4 @@
import codeql.ruby.security.performance.RegExpTreeView
import codeql.ruby.Regexp
query predicate nonUniqueChild(RegExpParent parent, int i, RegExpTerm child) {
child = parent.getChild(i) and

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The `ParseRegExp` and `RegExpTreeView` modules are now "internal" modules. Users should use `codeql.ruby.Regexp` instead.

View File

@@ -0,0 +1,143 @@
/**
* Provides classes for working with regular expressions.
*
* Regular expression literals are represented as an abstract syntax tree of regular expression
* terms.
*/
import regexp.RegExpTreeView // re-export
private import regexp.internal.ParseRegExp
private import codeql.ruby.ast.Literal as AST
private import codeql.ruby.DataFlow
private import codeql.ruby.controlflow.CfgNodes
private import codeql.ruby.ApiGraphs
private import codeql.ruby.dataflow.internal.tainttrackingforlibraries.TaintTrackingImpl
/**
* Provides utility predicates related to regular expressions.
*/
module RegExpPatterns {
/**
* Gets a pattern that matches common top-level domain names in lower case.
*/
string getACommonTld() {
// according to ranking by http://google.com/search?q=site:.<<TLD>>
result = "(?:com|org|edu|gov|uk|net|io)(?![a-z0-9])"
}
}
/**
* A node whose value may flow to a position where it is interpreted
* as a part of a regular expression.
*/
abstract class RegExpPatternSource extends DataFlow::Node {
/**
* Gets a node where the pattern of this node is parsed as a part of
* a regular expression.
*/
abstract DataFlow::Node getAParse();
/**
* Gets the root term of the regular expression parsed from this pattern.
*/
abstract RegExpTerm getRegExpTerm();
}
/**
* A regular expression literal, viewed as the pattern source for itself.
*/
private class RegExpLiteralPatternSource extends RegExpPatternSource {
private AST::RegExpLiteral astNode;
RegExpLiteralPatternSource() { astNode = this.asExpr().getExpr() }
override DataFlow::Node getAParse() { result = this }
override RegExpTerm getRegExpTerm() { result = astNode.getParsed() }
}
/**
* A node whose string value may flow to a position where it is interpreted
* as a part of a regular expression.
*/
private class StringRegExpPatternSource extends RegExpPatternSource {
private DataFlow::Node parse;
StringRegExpPatternSource() { this = regExpSource(parse) }
override DataFlow::Node getAParse() { result = parse }
override RegExpTerm getRegExpTerm() { result.getRegExp() = this.asExpr().getExpr() }
}
private class RegExpLiteralRegExp extends RegExp, AST::RegExpLiteral {
override predicate isDotAll() { this.hasMultilineFlag() }
override predicate isIgnoreCase() { this.hasCaseInsensitiveFlag() }
override string getFlags() { result = this.getFlagString() }
}
private class ParsedStringRegExp extends RegExp {
private DataFlow::Node parse;
ParsedStringRegExp() { this = regExpSource(parse).asExpr().getExpr() }
DataFlow::Node getAParse() { result = parse }
override predicate isDotAll() { none() }
override predicate isIgnoreCase() { none() }
override string getFlags() { none() }
}
/**
* Holds if `source` may be interpreted as a regular expression.
*/
private predicate isInterpretedAsRegExp(DataFlow::Node source) {
// The first argument to an invocation of `Regexp.new` or `Regexp.compile`.
source = API::getTopLevelMember("Regexp").getAMethodCall(["compile", "new"]).getArgument(0)
or
// The argument of a call that coerces the argument to a regular expression.
exists(DataFlow::CallNode mce |
mce.getMethodName() = ["match", "match?"] and
source = mce.getArgument(0) and
// exclude https://ruby-doc.org/core-2.4.0/Regexp.html#method-i-match
not mce.getReceiver().asExpr().getExpr() instanceof AST::RegExpLiteral
)
}
private class RegExpConfiguration extends Configuration {
RegExpConfiguration() { this = "RegExpConfiguration" }
override predicate isSource(DataFlow::Node source) {
source.asExpr() =
any(ExprCfgNode e |
e.getConstantValue().isString(_) and
not e instanceof ExprNodes::VariableReadAccessCfgNode and
not e instanceof ExprNodes::ConstantReadAccessCfgNode
)
}
override predicate isSink(DataFlow::Node sink) { isInterpretedAsRegExp(sink) }
override predicate isSanitizer(DataFlow::Node node) {
// stop flow if `node` is receiver of
// https://ruby-doc.org/core-2.4.0/String.html#method-i-match
exists(DataFlow::CallNode mce |
mce.getMethodName() = ["match", "match?"] and
node = mce.getReceiver() and
mce.getArgument(0).asExpr().getExpr() instanceof AST::RegExpLiteral
)
}
}
/**
* Gets a node whose value may flow (inter-procedurally) to `re`, where it is interpreted
* as a part of a regular expression.
*/
cached
DataFlow::Node regExpSource(DataFlow::Node re) {
exists(RegExpConfiguration c | c.hasFlow(result, re))
}

View File

@@ -1,5 +1,5 @@
private import codeql.ruby.AST
private import codeql.ruby.security.performance.RegExpTreeView as RETV
private import codeql.ruby.Regexp as RE
private import internal.AST
private import internal.Constant
private import internal.Literal
@@ -393,7 +393,7 @@ class RegExpLiteral extends StringlikeLiteral instanceof RegExpLiteralImpl {
final predicate hasFreeSpacingFlag() { this.getFlagString().charAt(_) = "x" }
/** Returns the root node of the parse tree of this regular expression. */
final RETV::RegExpTerm getParsed() { result = RETV::getParsedRegExp(this) }
final RE::RegExpTerm getParsed() { result = RE::getParsedRegExp(this) }
}
/**

View File

@@ -7,7 +7,7 @@
*/
private import AST
private import codeql.ruby.security.performance.RegExpTreeView as RETV
private import codeql.ruby.Regexp as RE
private import codeql.ruby.ast.internal.Synthesis
/**
@@ -37,7 +37,7 @@ private predicate shouldPrintAstEdge(AstNode parent, string edgeName, AstNode ch
newtype TPrintNode =
TPrintRegularAstNode(AstNode n) { shouldPrintNode(n) } or
TPrintRegExpNode(RETV::RegExpTerm term) {
TPrintRegExpNode(RE::RegExpTerm term) {
exists(RegExpLiteral literal |
shouldPrintNode(literal) and
term.getRootTerm() = literal.getParsed()
@@ -107,7 +107,7 @@ class PrintRegularAstNode extends PrintAstNode, TPrintRegularAstNode {
or
// If this AST node is a regexp literal, add the parsed regexp tree as a
// child.
exists(RETV::RegExpTerm t | t = astNode.(RegExpLiteral).getParsed() |
exists(RE::RegExpTerm t | t = astNode.(RegExpLiteral).getParsed() |
result = TPrintRegExpNode(t) and edgeName = "getParsed"
)
}
@@ -134,7 +134,7 @@ class PrintRegularAstNode extends PrintAstNode, TPrintRegularAstNode {
/** A parsed regexp node in the output tree. */
class PrintRegExpNode extends PrintAstNode, TPrintRegExpNode {
RETV::RegExpTerm regexNode;
RE::RegExpTerm regexNode;
PrintRegExpNode() { this = TPrintRegExpNode(regexNode) }
@@ -147,7 +147,7 @@ class PrintRegExpNode extends PrintAstNode, TPrintRegExpNode {
exists(int i | result = TPrintRegExpNode(regexNode.getChild(i)) and edgeName = i.toString())
}
override int getOrder() { exists(RETV::RegExpTerm p | p.getChild(result) = regexNode) }
override int getOrder() { exists(RE::RegExpTerm p | p.getChild(result) = regexNode) }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More