mirror of
https://github.com/github/codeql.git
synced 2026-07-20 18:58:36 +02:00
Merge remote-tracking branch 'origin/main' into nickrolfe/constant_regexp
This commit is contained in:
16
.github/workflows/ql-for-ql-build.yml
vendored
16
.github/workflows/ql-for-ql-build.yml
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
2
.github/workflows/ql-for-ql-tests.yml
vendored
2
.github/workflows/ql-for-ql-tests.yml
vendored
@@ -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
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
@@ -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()
|
||||
@@ -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);
|
||||
@@ -0,0 +1,5 @@
|
||||
char* key = getenv("APP_KEY");
|
||||
|
||||
//...
|
||||
|
||||
fprintf(stderr, "Key not recognized: %s\n", key);
|
||||
342
cpp/ql/src/Security/CWE/CWE-497/SystemData.qll
Normal file
342
cpp/ql/src/Security/CWE/CWE-497/SystemData.qll
Normal 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).*")
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
@@ -3,4 +3,3 @@
|
||||
- apply: code-scanning-selectors.yml
|
||||
from: codeql/suite-helpers
|
||||
- apply: codeql-suites/exclude-slow-queries.yml
|
||||
from: codeql/cpp-queries
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -3,4 +3,3 @@
|
||||
- apply: security-extended-selectors.yml
|
||||
from: codeql/suite-helpers
|
||||
- apply: codeql-suites/exclude-slow-queries.yml
|
||||
from: codeql/cpp-queries
|
||||
|
||||
@@ -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. |
|
||||
|
||||
@@ -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 | |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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&&)
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 :
|
||||
|
||||
@@ -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 | { ... } |
|
||||
|
||||
@@ -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. |
|
||||
|
||||
@@ -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 |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-497/PotentiallyExposedSystemData.ql
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-497/PotentiallyExposedSystemData.ql
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
// ...
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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()
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:. |
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
203
misc/scripts/models-as-data/generate_flow_model.py
Normal file
203
misc/scripts/models-as-data/generate_flow_model.py
Normal 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)
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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:
|
||||
*
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
16
python/ql/src/meta/analysis-quality/CallGraph.ql
Normal file
16
python/ql/src/meta/analysis-quality/CallGraph.ql
Normal 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()
|
||||
132
python/ql/test/TestUtilities/VerifyApiGraphs.qll
Normal file
132
python/ql/test/TestUtilities/VerifyApiGraphs.qll
Normal 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() }
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
foo = 42
|
||||
@@ -1 +0,0 @@
|
||||
pass
|
||||
@@ -1 +0,0 @@
|
||||
pass
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
import TestUtilities.VerifyApiGraphs
|
||||
61
python/ql/test/library-tests/ApiGraphs/py3/deftest1.py
Normal file
61
python/ql/test/library-tests/ApiGraphs/py3/deftest1.py
Normal 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)
|
||||
19
python/ql/test/library-tests/ApiGraphs/py3/deftest2.py
Normal file
19
python/ql/test/library-tests/ApiGraphs/py3/deftest2.py
Normal 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()
|
||||
@@ -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():
|
||||
@@ -0,0 +1 @@
|
||||
import TestUtilities.VerifyApiGraphs
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
4
ruby/ql/lib/change-notes/2022-03-21-regex.md
Normal file
4
ruby/ql/lib/change-notes/2022-03-21-regex.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `ParseRegExp` and `RegExpTreeView` modules are now "internal" modules. Users should use `codeql.ruby.Regexp` instead.
|
||||
143
ruby/ql/lib/codeql/ruby/Regexp.qll
Normal file
143
ruby/ql/lib/codeql/ruby/Regexp.qll
Normal 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))
|
||||
}
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
1030
ruby/ql/lib/codeql/ruby/regexp/RegExpTreeView.qll
Normal file
1030
ruby/ql/lib/codeql/ruby/regexp/RegExpTreeView.qll
Normal file
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
Reference in New Issue
Block a user