mirror of
https://github.com/github/codeql.git
synced 2026-04-27 17:55:19 +02:00
Merge branch 'experimental-manually-check-request-verb' of https://github.com/thiggy1342/codeql into experimental-manually-check-request-verb
This commit is contained in:
@@ -41,7 +41,7 @@ jobs:
|
||||
git log -1 --format='%H'
|
||||
working-directory: base
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v3
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.8
|
||||
- name: Download CodeQL CLI
|
||||
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
- name: Clone self (github/codeql)
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v3
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.8
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
path: codeqlModels
|
||||
fetch-depth: 0
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v3
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.8
|
||||
- name: Download CodeQL CLI
|
||||
|
||||
2
.github/workflows/csv-coverage-update.yml
vendored
2
.github/workflows/csv-coverage-update.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
path: ql
|
||||
fetch-depth: 0
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v3
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.8
|
||||
- name: Download CodeQL CLI
|
||||
|
||||
2
.github/workflows/csv-coverage.yml
vendored
2
.github/workflows/csv-coverage.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
path: codeqlModels
|
||||
ref: ${{ github.event.inputs.qlModelShaOverride || github.ref }}
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v3
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.8
|
||||
- name: Download CodeQL CLI
|
||||
|
||||
2
.github/workflows/query-list.yml
vendored
2
.github/workflows/query-list.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
with:
|
||||
path: codeql
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v3
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.8
|
||||
- name: Download CodeQL CLI
|
||||
|
||||
2
.github/workflows/swift-codegen.yml
vendored
2
.github/workflows/swift-codegen.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
run: |
|
||||
bazel run //swift/codegen
|
||||
git add swift
|
||||
git diff --exit-code --stat HEAD
|
||||
git diff --exit-code HEAD
|
||||
- name: Generate C++ files
|
||||
run: |
|
||||
bazel run //swift/codegen:codegen -- --generate=trap,cpp --cpp-output=$PWD/swift-generated-headers
|
||||
|
||||
@@ -75,7 +75,8 @@
|
||||
"DataFlow Java/C# Flow Summaries": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll"
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImpl.qll"
|
||||
],
|
||||
"SsaReadPosition Java/C#": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll",
|
||||
@@ -527,7 +528,8 @@
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/AccessPathSyntax.qll",
|
||||
"javascript/ql/lib/semmle/javascript/frameworks/data/internal/AccessPathSyntax.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/AccessPathSyntax.qll",
|
||||
"python/ql/lib/semmle/python/frameworks/data/internal/AccessPathSyntax.qll"
|
||||
"python/ql/lib/semmle/python/frameworks/data/internal/AccessPathSyntax.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/AccessPathSyntax.qll"
|
||||
],
|
||||
"IncompleteUrlSubstringSanitization": [
|
||||
"javascript/ql/src/Security/CWE-020/IncompleteUrlSubstringSanitization.qll",
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
|
||||
int *global_ptr;
|
||||
const char *global_string = "hello, world";
|
||||
|
||||
void test1(int *ptr, int &ref)
|
||||
{
|
||||
const char *str;
|
||||
int v, *p;
|
||||
char c;
|
||||
|
||||
v = *ptr; // `ptr` dereferenced
|
||||
v = ptr[0]; // `ptr` dereferenced
|
||||
p = ptr;
|
||||
|
||||
*ptr = 0; // `ptr` dereferenced
|
||||
ptr[0] = 0; // `ptr` dereferenced
|
||||
ptr = 0;
|
||||
|
||||
(*ptr)++; // `ptr`, `*ptr` dereferenced
|
||||
*(ptr++); // `ptr++` dereferenced
|
||||
ptr++;
|
||||
|
||||
v = ref; // (`ref` implicitly dereferenced, not detected)
|
||||
p = &ref;
|
||||
ref = 0; // (`ref` implicitly dereferenced, not detected)
|
||||
ref++; // (`ref` implicitly dereferenced, not detected)
|
||||
|
||||
*global_ptr; // `global_ptr` dereferenced
|
||||
str = global_string;
|
||||
c = global_string[5]; // `global_string` dereferenced
|
||||
}
|
||||
|
||||
struct myStruct
|
||||
{
|
||||
int x;
|
||||
void f() {};
|
||||
void (*g)();
|
||||
};
|
||||
|
||||
void test1(myStruct *ms)
|
||||
{
|
||||
void (*h)();
|
||||
|
||||
ms;
|
||||
ms->x; // `ms` dereferenced
|
||||
ms->f(); // `ms` dereferenced
|
||||
ms->g(); // `ms` dereferenced
|
||||
h = ms->g; // `ms` dereferenced
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
| dereferenced.cpp:11:6:11:9 | * ... | dereferenced.cpp:11:7:11:9 | ptr |
|
||||
| dereferenced.cpp:12:6:12:11 | access to array | dereferenced.cpp:12:6:12:8 | ptr |
|
||||
| dereferenced.cpp:15:2:15:5 | * ... | dereferenced.cpp:15:3:15:5 | ptr |
|
||||
| dereferenced.cpp:16:2:16:7 | access to array | dereferenced.cpp:16:2:16:4 | ptr |
|
||||
| dereferenced.cpp:19:3:19:6 | * ... | dereferenced.cpp:19:4:19:6 | ptr |
|
||||
| dereferenced.cpp:19:4:19:6 | ptr | dereferenced.cpp:19:3:19:6 | * ... |
|
||||
| dereferenced.cpp:20:2:20:9 | * ... | dereferenced.cpp:20:4:20:8 | ... ++ |
|
||||
| dereferenced.cpp:28:2:28:12 | * ... | dereferenced.cpp:28:3:28:12 | global_ptr |
|
||||
| dereferenced.cpp:30:6:30:21 | access to array | dereferenced.cpp:30:6:30:18 | global_string |
|
||||
| dereferenced.cpp:45:6:45:6 | x | dereferenced.cpp:45:2:45:3 | ms |
|
||||
| dereferenced.cpp:46:6:46:6 | call to f | dereferenced.cpp:46:2:46:3 | ms |
|
||||
| dereferenced.cpp:47:6:47:6 | g | dereferenced.cpp:47:2:47:3 | ms |
|
||||
| dereferenced.cpp:48:10:48:10 | g | dereferenced.cpp:48:6:48:7 | ms |
|
||||
@@ -0,0 +1,6 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.Dereferenced
|
||||
|
||||
from Expr op, Expr e
|
||||
where dereferencedByOperation(op, e) // => dereferenced(e)
|
||||
select op, e
|
||||
@@ -218,10 +218,10 @@ postWithInFlow
|
||||
| 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. |
|
||||
| lambdas.cpp:23:3:23:14 | v [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| lambdas.cpp:23:15:23:15 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| 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. |
|
||||
|
||||
@@ -4833,6 +4833,9 @@
|
||||
| ir.cpp:1043:24:1043:24 | SideEffect | ~m1043_20 |
|
||||
| ir.cpp:1043:31:1043:31 | Address | &:r1043_9 |
|
||||
| ir.cpp:1043:36:1043:55 | Address | &:r1043_11 |
|
||||
| ir.cpp:1043:43:1043:43 | Address | &:r1043_16 |
|
||||
| ir.cpp:1043:43:1043:43 | Arg(this) | this:r1043_16 |
|
||||
| ir.cpp:1043:43:1043:43 | SideEffect | ~m1043_20 |
|
||||
| ir.cpp:1043:43:1043:54 | Address | &:r1043_22 |
|
||||
| ir.cpp:1043:43:1043:54 | Address | &:r1043_24 |
|
||||
| ir.cpp:1043:43:1043:54 | Address | &:r1043_25 |
|
||||
@@ -4853,11 +4856,8 @@
|
||||
| ir.cpp:1043:45:1043:49 | SideEffect | ~m1043_4 |
|
||||
| ir.cpp:1043:45:1043:49 | Unary | r1043_13 |
|
||||
| ir.cpp:1043:45:1043:49 | Unary | r1043_15 |
|
||||
| ir.cpp:1043:52:1043:52 | Address | &:r1043_16 |
|
||||
| ir.cpp:1043:52:1043:52 | Arg(this) | this:r1043_16 |
|
||||
| ir.cpp:1043:52:1043:52 | SideEffect | ~m1043_20 |
|
||||
| ir.cpp:1043:54:1043:54 | Load | ~m1043_20 |
|
||||
| ir.cpp:1043:54:1043:54 | Right | r1043_26 |
|
||||
| ir.cpp:1043:53:1043:53 | Load | ~m1043_20 |
|
||||
| ir.cpp:1043:53:1043:53 | Right | r1043_26 |
|
||||
| ir.cpp:1043:58:1043:58 | ChiPartial | partial:m1043_9 |
|
||||
| ir.cpp:1043:58:1043:58 | ChiTotal | total:m1043_3 |
|
||||
| ir.cpp:1043:58:1043:58 | StoreValue | r1043_8 |
|
||||
@@ -4972,6 +4972,9 @@
|
||||
| ir.cpp:1047:34:1047:34 | SideEffect | ~m1047_20 |
|
||||
| ir.cpp:1047:41:1047:41 | Address | &:r1047_9 |
|
||||
| ir.cpp:1047:46:1047:65 | Address | &:r1047_11 |
|
||||
| ir.cpp:1047:53:1047:53 | Address | &:r1047_16 |
|
||||
| ir.cpp:1047:53:1047:53 | Arg(this) | this:r1047_16 |
|
||||
| ir.cpp:1047:53:1047:53 | SideEffect | ~m1047_20 |
|
||||
| ir.cpp:1047:53:1047:64 | Address | &:r1047_23 |
|
||||
| ir.cpp:1047:53:1047:64 | Load | ~m1047_20 |
|
||||
| ir.cpp:1047:53:1047:64 | StoreValue | r1047_24 |
|
||||
@@ -4986,9 +4989,6 @@
|
||||
| ir.cpp:1047:55:1047:59 | SideEffect | ~m1047_4 |
|
||||
| ir.cpp:1047:55:1047:59 | Unary | r1047_13 |
|
||||
| ir.cpp:1047:55:1047:59 | Unary | r1047_15 |
|
||||
| ir.cpp:1047:62:1047:62 | Address | &:r1047_16 |
|
||||
| ir.cpp:1047:62:1047:62 | Arg(this) | this:r1047_16 |
|
||||
| ir.cpp:1047:62:1047:62 | SideEffect | ~m1047_20 |
|
||||
| ir.cpp:1047:63:1047:63 | Right | r1047_22 |
|
||||
| ir.cpp:1047:68:1047:68 | StoreValue | r1047_8 |
|
||||
| ir.cpp:1047:68:1047:68 | Unary | r1047_7 |
|
||||
@@ -5097,6 +5097,9 @@
|
||||
| ir.cpp:1051:39:1051:39 | SideEffect | ~m1051_20 |
|
||||
| ir.cpp:1051:46:1051:46 | Address | &:r1051_9 |
|
||||
| ir.cpp:1051:51:1051:70 | Address | &:r1051_11 |
|
||||
| ir.cpp:1051:58:1051:58 | Address | &:r1051_16 |
|
||||
| ir.cpp:1051:58:1051:58 | Arg(this) | this:r1051_16 |
|
||||
| ir.cpp:1051:58:1051:58 | SideEffect | ~m1051_20 |
|
||||
| ir.cpp:1051:58:1051:69 | Address | &:r1051_22 |
|
||||
| ir.cpp:1051:58:1051:69 | Address | &:r1051_24 |
|
||||
| ir.cpp:1051:58:1051:69 | Address | &:r1051_26 |
|
||||
@@ -5117,9 +5120,6 @@
|
||||
| ir.cpp:1051:60:1051:64 | SideEffect | ~m1051_4 |
|
||||
| ir.cpp:1051:60:1051:64 | Unary | r1051_13 |
|
||||
| ir.cpp:1051:60:1051:64 | Unary | r1051_15 |
|
||||
| ir.cpp:1051:67:1051:67 | Address | &:r1051_16 |
|
||||
| ir.cpp:1051:67:1051:67 | Arg(this) | this:r1051_16 |
|
||||
| ir.cpp:1051:67:1051:67 | SideEffect | ~m1051_20 |
|
||||
| ir.cpp:1051:73:1051:73 | ChiPartial | partial:m1051_9 |
|
||||
| ir.cpp:1051:73:1051:73 | ChiTotal | total:m1051_3 |
|
||||
| ir.cpp:1051:73:1051:73 | StoreValue | r1051_8 |
|
||||
@@ -5184,6 +5184,9 @@
|
||||
| ir.cpp:1054:49:1054:49 | SideEffect | ~m1054_20 |
|
||||
| ir.cpp:1054:56:1054:56 | Address | &:r1054_9 |
|
||||
| ir.cpp:1054:61:1054:88 | Address | &:r1054_11 |
|
||||
| ir.cpp:1054:68:1054:68 | Address | &:r1054_16 |
|
||||
| ir.cpp:1054:68:1054:68 | Arg(this) | this:r1054_16 |
|
||||
| ir.cpp:1054:68:1054:68 | SideEffect | ~m1054_20 |
|
||||
| ir.cpp:1054:68:1054:87 | Address | &:r1054_37 |
|
||||
| ir.cpp:1054:68:1054:87 | Load | ~m1054_20 |
|
||||
| ir.cpp:1054:68:1054:87 | StoreValue | r1054_38 |
|
||||
@@ -5198,9 +5201,6 @@
|
||||
| ir.cpp:1054:70:1054:74 | SideEffect | ~m1054_4 |
|
||||
| ir.cpp:1054:70:1054:74 | Unary | r1054_13 |
|
||||
| ir.cpp:1054:70:1054:74 | Unary | r1054_15 |
|
||||
| ir.cpp:1054:77:1054:77 | Address | &:r1054_16 |
|
||||
| ir.cpp:1054:77:1054:77 | Arg(this) | this:r1054_16 |
|
||||
| ir.cpp:1054:77:1054:77 | SideEffect | ~m1054_20 |
|
||||
| ir.cpp:1054:78:1054:82 | Address | &:r1054_22 |
|
||||
| ir.cpp:1054:78:1054:82 | Address | &:r1054_24 |
|
||||
| ir.cpp:1054:78:1054:82 | Left | r1054_25 |
|
||||
|
||||
@@ -156,10 +156,10 @@
|
||||
| captures.cpp:23:12:23:16 | x |
|
||||
| captures.cpp:23:12:23:16 | y |
|
||||
| captures.cpp:23:12:23:20 | ... + ... |
|
||||
| captures.cpp:23:16:23:16 | (reference dereference) |
|
||||
| captures.cpp:23:16:23:16 | definition of y |
|
||||
| captures.cpp:23:16:23:16 | y |
|
||||
| captures.cpp:23:16:23:16 | y |
|
||||
| captures.cpp:23:18:23:18 | (reference dereference) |
|
||||
| captures.cpp:23:20:23:20 | z |
|
||||
| captures.cpp:26:3:26:24 | return ... |
|
||||
| captures.cpp:26:10:26:17 | (const lambda [] type at line 22, col. 19)... |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name Capture sink models.
|
||||
* @description Finds public methods that act as sinks as they flow into a a known sink.
|
||||
* @description Finds public methods that act as sinks as they flow into a known sink.
|
||||
* @kind diagnostic
|
||||
* @id cs/utils/model-generator/sink-models
|
||||
* @tags model-generator
|
||||
|
||||
@@ -291,7 +291,7 @@ func main() {
|
||||
}
|
||||
|
||||
// Go 1.16 and later won't automatically attempt to update go.mod / go.sum during package loading, so try to update them here:
|
||||
if depMode == GoGetWithModules && semver.Compare(getEnvGoSemVer(), "1.16") >= 0 {
|
||||
if modMode != ModVendor && depMode == GoGetWithModules && semver.Compare(getEnvGoSemVer(), "1.16") >= 0 {
|
||||
// stat go.mod and go.sum
|
||||
beforeGoModFileInfo, beforeGoModErr := os.Stat("go.mod")
|
||||
if beforeGoModErr != nil {
|
||||
|
||||
@@ -15,11 +15,11 @@ def is_windows():
|
||||
return False
|
||||
|
||||
def version_tuple_to_string(version):
|
||||
return f'{version[0]}.{version[1]}.{version[2]}'
|
||||
return f'{version[0]}.{version[1]}.{version[2]}{version[3]}'
|
||||
|
||||
def version_string_to_tuple(version):
|
||||
m = re.match(r'([0-9]+)\.([0-9]+)\.([0-9]+)', version)
|
||||
return tuple([int(m.group(i)) for i in range(1, 4)])
|
||||
m = re.match(r'([0-9]+)\.([0-9]+)\.([0-9]+)(.*)', version)
|
||||
return tuple([int(m.group(i)) for i in range(1, 4)] + [m.group(4)])
|
||||
|
||||
many_versions = [ '1.4.32', '1.5.0', '1.5.10', '1.5.21', '1.5.31', '1.6.10', '1.7.0-RC', '1.6.20' ]
|
||||
|
||||
|
||||
@@ -21,7 +21,9 @@ import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaClass
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaMethod
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameterListOwner
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
@@ -514,7 +516,7 @@ open class KotlinFileExtractor(
|
||||
else
|
||||
null
|
||||
} ?: vp.type
|
||||
val javaType = ((vp.parent as? IrFunction)?.let { getJavaMethod(it) })?.valueParameters?.getOrNull(idx)?.type
|
||||
val javaType = (vp.parent as? IrFunction)?.let { getJavaCallable(it)?.let { jCallable -> getJavaValueParameterType(jCallable, idx) } }
|
||||
val typeWithWildcards = addJavaLoweringWildcards(maybeErasedType, !hasWildcardSuppressionAnnotation(vp), javaType)
|
||||
val substitutedType = typeSubstitution?.let { it(typeWithWildcards, TypeContext.OTHER, pluginContext) } ?: typeWithWildcards
|
||||
val id = useValueParameter(vp, parent)
|
||||
@@ -691,8 +693,8 @@ open class KotlinFileExtractor(
|
||||
with("function", f) {
|
||||
DeclarationStackAdjuster(f).use {
|
||||
|
||||
val javaMethod = getJavaMethod(f)
|
||||
getFunctionTypeParameters(f).mapIndexed { idx, tp -> extractTypeParameter(tp, idx, javaMethod?.typeParameters?.getOrNull(idx)) }
|
||||
val javaCallable = getJavaCallable(f)
|
||||
getFunctionTypeParameters(f).mapIndexed { idx, tp -> extractTypeParameter(tp, idx, (javaCallable as? JavaTypeParameterListOwner)?.typeParameters?.getOrNull(idx)) }
|
||||
|
||||
val id =
|
||||
idOverride
|
||||
@@ -726,7 +728,7 @@ open class KotlinFileExtractor(
|
||||
|
||||
val paramsSignature = allParamTypes.joinToString(separator = ",", prefix = "(", postfix = ")") { it.javaResult.signature!! }
|
||||
|
||||
val adjustedReturnType = addJavaLoweringWildcards(getAdjustedReturnType(f), false, javaMethod?.returnType)
|
||||
val adjustedReturnType = addJavaLoweringWildcards(getAdjustedReturnType(f), false, (javaCallable as? JavaMethod)?.returnType)
|
||||
val substReturnType = typeSubstitution?.let { it(adjustedReturnType, TypeContext.RETURN, pluginContext) } ?: adjustedReturnType
|
||||
|
||||
val locId = locOverride ?: getLocation(f, classTypeArgsIncludingOuterClasses)
|
||||
|
||||
@@ -9,7 +9,6 @@ import org.jetbrains.kotlin.backend.common.ir.allOverridden
|
||||
import org.jetbrains.kotlin.backend.common.ir.isFinalClass
|
||||
import org.jetbrains.kotlin.backend.common.lower.parents
|
||||
import org.jetbrains.kotlin.backend.common.lower.parentsWithSelf
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.getJvmNameFromAnnotation
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.propertyIfAccessor
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
@@ -67,62 +66,6 @@ open class KotlinUsesExtractor(
|
||||
TypeResult(fakeKotlinType(), "", "")
|
||||
)
|
||||
|
||||
private data class MethodKey(val className: FqName, val functionName: Name)
|
||||
|
||||
private fun makeDescription(className: FqName, functionName: String) = MethodKey(className, Name.guessByFirstCharacter(functionName))
|
||||
|
||||
// This essentially mirrors SpecialBridgeMethods.kt, a backend pass which isn't easily available to our extractor.
|
||||
private val specialFunctions = mapOf(
|
||||
makeDescription(StandardNames.FqNames.collection, "<get-size>") to "size",
|
||||
makeDescription(FqName("java.util.Collection"), "<get-size>") to "size",
|
||||
makeDescription(StandardNames.FqNames.map, "<get-size>") to "size",
|
||||
makeDescription(FqName("java.util.Map"), "<get-size>") to "size",
|
||||
makeDescription(StandardNames.FqNames.charSequence.toSafe(), "<get-length>") to "length",
|
||||
makeDescription(FqName("java.lang.CharSequence"), "<get-length>") to "length",
|
||||
makeDescription(StandardNames.FqNames.map, "<get-keys>") to "keySet",
|
||||
makeDescription(FqName("java.util.Map"), "<get-keys>") to "keySet",
|
||||
makeDescription(StandardNames.FqNames.map, "<get-values>") to "values",
|
||||
makeDescription(FqName("java.util.Map"), "<get-values>") to "values",
|
||||
makeDescription(StandardNames.FqNames.map, "<get-entries>") to "entrySet",
|
||||
makeDescription(FqName("java.util.Map"), "<get-entries>") to "entrySet"
|
||||
)
|
||||
|
||||
private val specialFunctionShortNames = specialFunctions.keys.map { it.functionName }.toSet()
|
||||
|
||||
fun getSpecialJvmName(f: IrFunction): String? {
|
||||
if (specialFunctionShortNames.contains(f.name) && f is IrSimpleFunction) {
|
||||
f.allOverridden(true).forEach { overriddenFunc ->
|
||||
overriddenFunc.parentAsClass.fqNameWhenAvailable?.let { parentFqName ->
|
||||
specialFunctions[MethodKey(parentFqName, f.name)]?.let {
|
||||
return it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun getJvmName(container: IrAnnotationContainer): String? {
|
||||
for(a: IrConstructorCall in container.annotations) {
|
||||
val t = a.type
|
||||
if (t is IrSimpleType && a.valueArgumentsCount == 1) {
|
||||
val owner = t.classifier.owner
|
||||
val v = a.getValueArgument(0)
|
||||
if (owner is IrClass) {
|
||||
val aPkg = owner.packageFqName?.asString()
|
||||
val name = owner.name.asString()
|
||||
if(aPkg == "kotlin.jvm" && name == "JvmName" && v is IrConst<*>) {
|
||||
val value = v.value
|
||||
if (value is String) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (container as? IrFunction)?.let { getSpecialJvmName(container) }
|
||||
}
|
||||
|
||||
@OptIn(kotlin.ExperimentalStdlibApi::class) // Annotation required by kotlin versions < 1.5
|
||||
fun extractFileClass(f: IrFile): Label<out DbClass> {
|
||||
val fileName = f.fileEntry.name
|
||||
@@ -996,7 +939,7 @@ open class KotlinUsesExtractor(
|
||||
getFunctionTypeParameters(f),
|
||||
classTypeArgsIncludingOuterClasses,
|
||||
overridesCollectionsMethodWithAlteredParameterTypes(f),
|
||||
getJavaMethod(f),
|
||||
getJavaCallable(f),
|
||||
!hasWildcardSuppressionAnnotation(f)
|
||||
)
|
||||
|
||||
@@ -1028,7 +971,7 @@ open class KotlinUsesExtractor(
|
||||
// parameter erasure to match the way this class will appear to an external consumer of the .class file.
|
||||
overridesCollectionsMethod: Boolean,
|
||||
// The Java signature of this callable, if known.
|
||||
javaSignature: JavaMethod?,
|
||||
javaSignature: JavaMember?,
|
||||
// If true, Java wildcards implied by Kotlin type parameter variance should be added by default to this function's value parameters' types.
|
||||
// (Return-type wildcard addition is always off by default)
|
||||
addParameterWildcardsByDefault: Boolean,
|
||||
@@ -1056,7 +999,7 @@ open class KotlinUsesExtractor(
|
||||
// If this has happened, erase the type again to get the correct Java signature.
|
||||
val maybeAmendedForCollections = if (overridesCollectionsMethod) eraseCollectionsMethodParameterType(it.value.type, name, it.index) else it.value.type
|
||||
// Add any wildcard types that the Kotlin compiler would add in the Java lowering of this function:
|
||||
val withAddedWildcards = addJavaLoweringWildcards(maybeAmendedForCollections, addParameterWildcardsByDefault, javaSignature?.let { sig -> sig.valueParameters[it.index].type })
|
||||
val withAddedWildcards = addJavaLoweringWildcards(maybeAmendedForCollections, addParameterWildcardsByDefault, javaSignature?.let { sig -> getJavaValueParameterType(sig, it.index) })
|
||||
// Now substitute any class type parameters in:
|
||||
val maybeSubbed = withAddedWildcards.substituteTypeAndArguments(substitutionMap, TypeContext.OTHER, pluginContext)
|
||||
// Finally, mimic the Java extractor's behaviour by naming functions with type parameters for their erased types;
|
||||
@@ -1103,7 +1046,13 @@ open class KotlinUsesExtractor(
|
||||
}
|
||||
|
||||
@OptIn(ObsoleteDescriptorBasedAPI::class)
|
||||
fun getJavaMethod(f: IrFunction) = (f.descriptor.source as? JavaSourceElement)?.javaElement as? JavaMethod
|
||||
fun getJavaCallable(f: IrFunction) = (f.descriptor.source as? JavaSourceElement)?.javaElement as? JavaMember
|
||||
|
||||
fun getJavaValueParameterType(m: JavaMember, idx: Int) = when(m) {
|
||||
is JavaMethod -> m.valueParameters[idx].type
|
||||
is JavaConstructor -> m.valueParameters[idx].type
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun hasWildcardSuppressionAnnotation(d: IrDeclaration) =
|
||||
d.hasAnnotation(jvmWildcardSuppressionAnnotaton) ||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.github.codeql
|
||||
|
||||
import com.github.codeql.utils.getJvmName
|
||||
import com.intellij.openapi.vfs.StandardFileSystems
|
||||
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass
|
||||
@@ -18,17 +19,19 @@ import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource
|
||||
// and declarations within them into the parent class' JLS 13.1 name as
|
||||
// specified above, followed by a `$` separator and then the short name
|
||||
// for `that`.
|
||||
private fun getName(d: IrDeclarationWithName) = (d as? IrAnnotationContainer)?.let { getJvmName(it) } ?: d.name.asString()
|
||||
|
||||
fun getIrDeclBinaryName(that: IrDeclaration): String {
|
||||
val shortName = when(that) {
|
||||
is IrDeclarationWithName -> that.name.asString()
|
||||
is IrDeclarationWithName -> getName(that)
|
||||
else -> "(unknown-name)"
|
||||
}
|
||||
val internalName = StringBuilder(shortName);
|
||||
generateSequence(that.parent) { (it as? IrDeclaration)?.parent }
|
||||
.forEach {
|
||||
when (it) {
|
||||
is IrClass -> internalName.insert(0, it.name.asString() + "$")
|
||||
is IrPackageFragment -> it.fqName.asString().takeIf { it.isNotEmpty() }?.let { internalName.insert(0, "$it.") }
|
||||
is IrClass -> internalName.insert(0, getName(it) + "$")
|
||||
is IrPackageFragment -> it.fqName.asString().takeIf { fqName -> fqName.isNotEmpty() }?.let { fqName -> internalName.insert(0, "$fqName.") }
|
||||
}
|
||||
}
|
||||
return internalName.toString()
|
||||
|
||||
90
java/kotlin-extractor/src/main/kotlin/utils/JvmNames.kt
Normal file
90
java/kotlin-extractor/src/main/kotlin/utils/JvmNames.kt
Normal file
@@ -0,0 +1,90 @@
|
||||
package com.github.codeql.utils
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.allOverridden
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.util.packageFqName
|
||||
import org.jetbrains.kotlin.ir.util.parentClassOrNull
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
private data class MethodKey(val className: FqName, val functionName: Name)
|
||||
|
||||
private fun makeDescription(className: FqName, functionName: String) = MethodKey(className, Name.guessByFirstCharacter(functionName))
|
||||
|
||||
// This essentially mirrors SpecialBridgeMethods.kt, a backend pass which isn't easily available to our extractor.
|
||||
private val specialFunctions = mapOf(
|
||||
makeDescription(StandardNames.FqNames.collection, "<get-size>") to "size",
|
||||
makeDescription(FqName("java.util.Collection"), "<get-size>") to "size",
|
||||
makeDescription(StandardNames.FqNames.map, "<get-size>") to "size",
|
||||
makeDescription(FqName("java.util.Map"), "<get-size>") to "size",
|
||||
makeDescription(StandardNames.FqNames.charSequence.toSafe(), "<get-length>") to "length",
|
||||
makeDescription(FqName("java.lang.CharSequence"), "<get-length>") to "length",
|
||||
makeDescription(StandardNames.FqNames.map, "<get-keys>") to "keySet",
|
||||
makeDescription(FqName("java.util.Map"), "<get-keys>") to "keySet",
|
||||
makeDescription(StandardNames.FqNames.map, "<get-values>") to "values",
|
||||
makeDescription(FqName("java.util.Map"), "<get-values>") to "values",
|
||||
makeDescription(StandardNames.FqNames.map, "<get-entries>") to "entrySet",
|
||||
makeDescription(FqName("java.util.Map"), "<get-entries>") to "entrySet",
|
||||
makeDescription(StandardNames.FqNames.mutableList, "removeAt") to "remove",
|
||||
makeDescription(FqName("java.util.List"), "removeAt") to "remove",
|
||||
makeDescription(StandardNames.FqNames._enum.toSafe(), "<get-ordinal>") to "ordinal",
|
||||
makeDescription(FqName("java.lang.Enum"), "<get-ordinal>") to "ordinal",
|
||||
makeDescription(StandardNames.FqNames._enum.toSafe(), "<get-name>") to "name",
|
||||
makeDescription(FqName("java.lang.Enum"), "<get-name>") to "name",
|
||||
makeDescription(StandardNames.FqNames.number.toSafe(), "toByte") to "byteValue",
|
||||
makeDescription(FqName("java.lang.Number"), "toByte") to "byteValue",
|
||||
makeDescription(StandardNames.FqNames.number.toSafe(), "toShort") to "shortValue",
|
||||
makeDescription(FqName("java.lang.Number"), "toShort") to "shortValue",
|
||||
makeDescription(StandardNames.FqNames.number.toSafe(), "toInt") to "intValue",
|
||||
makeDescription(FqName("java.lang.Number"), "toInt") to "intValue",
|
||||
makeDescription(StandardNames.FqNames.number.toSafe(), "toLong") to "longValue",
|
||||
makeDescription(FqName("java.lang.Number"), "toLong") to "longValue",
|
||||
makeDescription(StandardNames.FqNames.number.toSafe(), "toFloat") to "floatValue",
|
||||
makeDescription(FqName("java.lang.Number"), "toFloat") to "floatValue",
|
||||
makeDescription(StandardNames.FqNames.number.toSafe(), "toDouble") to "doubleValue",
|
||||
makeDescription(FqName("java.lang.Number"), "toDouble") to "doubleValue",
|
||||
)
|
||||
|
||||
private val specialFunctionShortNames = specialFunctions.keys.map { it.functionName }.toSet()
|
||||
|
||||
fun getSpecialJvmName(f: IrFunction): String? {
|
||||
if (specialFunctionShortNames.contains(f.name) && f is IrSimpleFunction) {
|
||||
f.allOverridden(true).forEach { overriddenFunc ->
|
||||
overriddenFunc.parentClassOrNull?.fqNameWhenAvailable?.let { parentFqName ->
|
||||
specialFunctions[MethodKey(parentFqName, f.name)]?.let {
|
||||
return it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun getJvmName(container: IrAnnotationContainer): String? {
|
||||
for(a: IrConstructorCall in container.annotations) {
|
||||
val t = a.type
|
||||
if (t is IrSimpleType && a.valueArgumentsCount == 1) {
|
||||
val owner = t.classifier.owner
|
||||
val v = a.getValueArgument(0)
|
||||
if (owner is IrClass) {
|
||||
val aPkg = owner.packageFqName?.asString()
|
||||
val name = owner.name.asString()
|
||||
if(aPkg == "kotlin.jvm" && name == "JvmName" && v is IrConst<*>) {
|
||||
val value = v.value
|
||||
if (value is String) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (container as? IrFunction)?.let { getSpecialJvmName(container) }
|
||||
}
|
||||
@@ -304,6 +304,33 @@ class ContentSet instanceof Content {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the guard `g` validates the expression `e` upon evaluating to `branch`.
|
||||
*
|
||||
* The expression `e` is expected to be a syntactic part of the guard `g`.
|
||||
* For example, the guard `g` might be a call `isSafe(x)` and the expression `e`
|
||||
* the argument `x`.
|
||||
*/
|
||||
signature predicate guardChecksSig(Guard g, Expr e, boolean branch);
|
||||
|
||||
/**
|
||||
* Provides a set of barrier nodes for a guard that validates an expression.
|
||||
*
|
||||
* This is expected to be used in `isBarrier`/`isSanitizer` definitions
|
||||
* in data flow and taint tracking.
|
||||
*/
|
||||
module BarrierGuard<guardChecksSig/3 guardChecks> {
|
||||
/** Gets a node that is safely guarded by the given guard check. */
|
||||
Node getABarrierNode() {
|
||||
exists(Guard g, SsaVariable v, boolean branch, RValue use |
|
||||
guardChecks(g, v.getAUse(), branch) and
|
||||
use = v.getAUse() and
|
||||
g.controls(use.getBasicBlock(), branch) and
|
||||
result.asExpr() = use
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A guard that validates some expression.
|
||||
*
|
||||
|
||||
@@ -20,5 +20,5 @@ where
|
||||
c.fromSource() and
|
||||
exists(sc.getBody()) and
|
||||
not exists(CloneMethod ssc | sc.callsSuper(ssc))
|
||||
select sc, "This clone method does not call super.clone(), but is " + "overridden and called $@.",
|
||||
c, "in a subclass"
|
||||
select sc, "This clone method does not call super.clone(), but is overridden and called $@.", c,
|
||||
"in a subclass"
|
||||
|
||||
@@ -17,4 +17,4 @@ from Method m
|
||||
where
|
||||
m.hasName(m.getDeclaringType().getName()) and
|
||||
m.fromSource()
|
||||
select m, "This method has the same name as its declaring class." + " Should it be a constructor?"
|
||||
select m, "This method has the same name as its declaring class. Should it be a constructor?"
|
||||
|
||||
@@ -96,5 +96,4 @@ where
|
||||
not exceptions(c, f) and
|
||||
reason = nonSerialReason(f.getType())
|
||||
select f,
|
||||
"This field is in a serializable class, " + " but is not serializable itself because " + reason +
|
||||
"."
|
||||
"This field is in a serializable class, but is not serializable itself because " + reason + "."
|
||||
|
||||
@@ -19,15 +19,13 @@ import semmle.code.java.security.PathCreation
|
||||
import DataFlow::PathGraph
|
||||
import TaintedPathCommon
|
||||
|
||||
class ContainsDotDotSanitizer extends DataFlow::BarrierGuard {
|
||||
ContainsDotDotSanitizer() {
|
||||
this.(MethodAccess).getMethod().hasName("contains") and
|
||||
this.(MethodAccess).getAnArgument().(StringLiteral).getValue() = ".."
|
||||
}
|
||||
|
||||
override predicate checks(Expr e, boolean branch) {
|
||||
e = this.(MethodAccess).getQualifier() and branch = false
|
||||
}
|
||||
predicate containsDotDotSanitizer(Guard g, Expr e, boolean branch) {
|
||||
exists(MethodAccess contains | g = contains |
|
||||
contains.getMethod().hasName("contains") and
|
||||
contains.getAnArgument().(StringLiteral).getValue() = ".." and
|
||||
e = contains.getQualifier() and
|
||||
branch = false
|
||||
)
|
||||
}
|
||||
|
||||
class TaintedPathConfig extends TaintTracking::Configuration {
|
||||
@@ -41,10 +39,8 @@ class TaintedPathConfig extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
exists(Type t | t = node.getType() | t instanceof BoxedType or t instanceof PrimitiveType)
|
||||
}
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof ContainsDotDotSanitizer
|
||||
or
|
||||
node = DataFlow::BarrierGuard<containsDotDotSanitizer/3>::getABarrierNode()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name Capture sink models.
|
||||
* @description Finds public methods that act as sinks as they flow into a a known sink.
|
||||
* @description Finds public methods that act as sinks as they flow into a known sink.
|
||||
* @kind diagnostic
|
||||
* @id java/utils/model-generator/sink-models
|
||||
* @tags model-generator
|
||||
|
||||
1
java/ql/test/kotlin/library-tests/enum/enumUser.kt
Normal file
1
java/ql/test/kotlin/library-tests/enum/enumUser.kt
Normal file
@@ -0,0 +1 @@
|
||||
fun usesEnum(e: Enum<*>) = e.ordinal.toString() + e.name
|
||||
27
java/ql/test/kotlin/library-tests/enum/test.expected
Normal file
27
java/ql/test/kotlin/library-tests/enum/test.expected
Normal file
@@ -0,0 +1,27 @@
|
||||
| addAll |
|
||||
| addRange |
|
||||
| allOf |
|
||||
| asIterator |
|
||||
| clone |
|
||||
| compareTo |
|
||||
| complement |
|
||||
| complementOf |
|
||||
| copyOf |
|
||||
| describeConstable |
|
||||
| equals |
|
||||
| finalize |
|
||||
| getDeclaringClass |
|
||||
| hasMoreElements |
|
||||
| hashCode |
|
||||
| name |
|
||||
| nextElement |
|
||||
| noneOf |
|
||||
| of |
|
||||
| ordinal |
|
||||
| range |
|
||||
| resolveConstantDesc |
|
||||
| toString |
|
||||
| typeCheck |
|
||||
| usesEnum |
|
||||
| valueOf |
|
||||
| writeReplace |
|
||||
5
java/ql/test/kotlin/library-tests/enum/test.ql
Normal file
5
java/ql/test/kotlin/library-tests/enum/test.ql
Normal file
@@ -0,0 +1,5 @@
|
||||
import java
|
||||
|
||||
from Method m
|
||||
where m.getDeclaringType().getName().matches("Enum%")
|
||||
select m.getName()
|
||||
@@ -2057,44 +2057,44 @@ exprs.kt:
|
||||
# 39| 27: [LocalVariableDeclStmt] var ...;
|
||||
# 39| 1: [LocalVariableDeclExpr] by6
|
||||
# 39| 0: [ValueEQExpr] ... (value equals) ...
|
||||
# 39| 0: [MethodAccess] toInt(...)
|
||||
# 39| 0: [MethodAccess] intValue(...)
|
||||
# 39| -1: [VarAccess] byx
|
||||
# 39| 1: [MethodAccess] toInt(...)
|
||||
# 39| 1: [MethodAccess] intValue(...)
|
||||
# 39| -1: [VarAccess] byy
|
||||
# 40| 28: [LocalVariableDeclStmt] var ...;
|
||||
# 40| 1: [LocalVariableDeclExpr] by7
|
||||
# 40| 0: [ValueNEExpr] ... (value not-equals) ...
|
||||
# 40| 0: [MethodAccess] toInt(...)
|
||||
# 40| 0: [MethodAccess] intValue(...)
|
||||
# 40| -1: [VarAccess] byx
|
||||
# 40| 1: [MethodAccess] toInt(...)
|
||||
# 40| 1: [MethodAccess] intValue(...)
|
||||
# 40| -1: [VarAccess] byy
|
||||
# 41| 29: [LocalVariableDeclStmt] var ...;
|
||||
# 41| 1: [LocalVariableDeclExpr] by8
|
||||
# 41| 0: [LTExpr] ... < ...
|
||||
# 41| 0: [MethodAccess] toInt(...)
|
||||
# 41| 0: [MethodAccess] intValue(...)
|
||||
# 41| -1: [VarAccess] byx
|
||||
# 41| 1: [MethodAccess] toInt(...)
|
||||
# 41| 1: [MethodAccess] intValue(...)
|
||||
# 41| -1: [VarAccess] byy
|
||||
# 42| 30: [LocalVariableDeclStmt] var ...;
|
||||
# 42| 1: [LocalVariableDeclExpr] by9
|
||||
# 42| 0: [LEExpr] ... <= ...
|
||||
# 42| 0: [MethodAccess] toInt(...)
|
||||
# 42| 0: [MethodAccess] intValue(...)
|
||||
# 42| -1: [VarAccess] byx
|
||||
# 42| 1: [MethodAccess] toInt(...)
|
||||
# 42| 1: [MethodAccess] intValue(...)
|
||||
# 42| -1: [VarAccess] byy
|
||||
# 43| 31: [LocalVariableDeclStmt] var ...;
|
||||
# 43| 1: [LocalVariableDeclExpr] by10
|
||||
# 43| 0: [GTExpr] ... > ...
|
||||
# 43| 0: [MethodAccess] toInt(...)
|
||||
# 43| 0: [MethodAccess] intValue(...)
|
||||
# 43| -1: [VarAccess] byx
|
||||
# 43| 1: [MethodAccess] toInt(...)
|
||||
# 43| 1: [MethodAccess] intValue(...)
|
||||
# 43| -1: [VarAccess] byy
|
||||
# 44| 32: [LocalVariableDeclStmt] var ...;
|
||||
# 44| 1: [LocalVariableDeclExpr] by11
|
||||
# 44| 0: [GEExpr] ... >= ...
|
||||
# 44| 0: [MethodAccess] toInt(...)
|
||||
# 44| 0: [MethodAccess] intValue(...)
|
||||
# 44| -1: [VarAccess] byx
|
||||
# 44| 1: [MethodAccess] toInt(...)
|
||||
# 44| 1: [MethodAccess] intValue(...)
|
||||
# 44| -1: [VarAccess] byy
|
||||
# 45| 33: [LocalVariableDeclStmt] var ...;
|
||||
# 45| 1: [LocalVariableDeclExpr] by12
|
||||
@@ -2132,44 +2132,44 @@ exprs.kt:
|
||||
# 53| 40: [LocalVariableDeclStmt] var ...;
|
||||
# 53| 1: [LocalVariableDeclExpr] s6
|
||||
# 53| 0: [ValueEQExpr] ... (value equals) ...
|
||||
# 53| 0: [MethodAccess] toInt(...)
|
||||
# 53| 0: [MethodAccess] intValue(...)
|
||||
# 53| -1: [VarAccess] sx
|
||||
# 53| 1: [MethodAccess] toInt(...)
|
||||
# 53| 1: [MethodAccess] intValue(...)
|
||||
# 53| -1: [VarAccess] sy
|
||||
# 54| 41: [LocalVariableDeclStmt] var ...;
|
||||
# 54| 1: [LocalVariableDeclExpr] s7
|
||||
# 54| 0: [ValueNEExpr] ... (value not-equals) ...
|
||||
# 54| 0: [MethodAccess] toInt(...)
|
||||
# 54| 0: [MethodAccess] intValue(...)
|
||||
# 54| -1: [VarAccess] sx
|
||||
# 54| 1: [MethodAccess] toInt(...)
|
||||
# 54| 1: [MethodAccess] intValue(...)
|
||||
# 54| -1: [VarAccess] sy
|
||||
# 55| 42: [LocalVariableDeclStmt] var ...;
|
||||
# 55| 1: [LocalVariableDeclExpr] s8
|
||||
# 55| 0: [LTExpr] ... < ...
|
||||
# 55| 0: [MethodAccess] toInt(...)
|
||||
# 55| 0: [MethodAccess] intValue(...)
|
||||
# 55| -1: [VarAccess] sx
|
||||
# 55| 1: [MethodAccess] toInt(...)
|
||||
# 55| 1: [MethodAccess] intValue(...)
|
||||
# 55| -1: [VarAccess] sy
|
||||
# 56| 43: [LocalVariableDeclStmt] var ...;
|
||||
# 56| 1: [LocalVariableDeclExpr] s9
|
||||
# 56| 0: [LEExpr] ... <= ...
|
||||
# 56| 0: [MethodAccess] toInt(...)
|
||||
# 56| 0: [MethodAccess] intValue(...)
|
||||
# 56| -1: [VarAccess] sx
|
||||
# 56| 1: [MethodAccess] toInt(...)
|
||||
# 56| 1: [MethodAccess] intValue(...)
|
||||
# 56| -1: [VarAccess] sy
|
||||
# 57| 44: [LocalVariableDeclStmt] var ...;
|
||||
# 57| 1: [LocalVariableDeclExpr] s10
|
||||
# 57| 0: [GTExpr] ... > ...
|
||||
# 57| 0: [MethodAccess] toInt(...)
|
||||
# 57| 0: [MethodAccess] intValue(...)
|
||||
# 57| -1: [VarAccess] sx
|
||||
# 57| 1: [MethodAccess] toInt(...)
|
||||
# 57| 1: [MethodAccess] intValue(...)
|
||||
# 57| -1: [VarAccess] sy
|
||||
# 58| 45: [LocalVariableDeclStmt] var ...;
|
||||
# 58| 1: [LocalVariableDeclExpr] s11
|
||||
# 58| 0: [GEExpr] ... >= ...
|
||||
# 58| 0: [MethodAccess] toInt(...)
|
||||
# 58| 0: [MethodAccess] intValue(...)
|
||||
# 58| -1: [VarAccess] sx
|
||||
# 58| 1: [MethodAccess] toInt(...)
|
||||
# 58| 1: [MethodAccess] intValue(...)
|
||||
# 58| -1: [VarAccess] sy
|
||||
# 59| 46: [LocalVariableDeclStmt] var ...;
|
||||
# 59| 1: [LocalVariableDeclExpr] s12
|
||||
|
||||
@@ -14,24 +14,24 @@
|
||||
| exprs.kt:36:15:36:23 | ... - ... | exprs.kt:36:15:36:17 | byx | exprs.kt:36:21:36:23 | byy |
|
||||
| exprs.kt:37:15:37:23 | ... / ... | exprs.kt:37:15:37:17 | byx | exprs.kt:37:21:37:23 | byy |
|
||||
| exprs.kt:38:15:38:23 | ... % ... | exprs.kt:38:15:38:17 | byx | exprs.kt:38:21:38:23 | byy |
|
||||
| exprs.kt:39:15:39:24 | ... (value equals) ... | exprs.kt:39:15:39:17 | toInt(...) | exprs.kt:39:22:39:24 | toInt(...) |
|
||||
| exprs.kt:40:15:40:24 | ... (value not-equals) ... | exprs.kt:40:15:40:17 | toInt(...) | exprs.kt:40:22:40:24 | toInt(...) |
|
||||
| exprs.kt:41:15:41:23 | ... < ... | exprs.kt:41:15:41:17 | toInt(...) | exprs.kt:41:21:41:23 | toInt(...) |
|
||||
| exprs.kt:42:15:42:24 | ... <= ... | exprs.kt:42:15:42:17 | toInt(...) | exprs.kt:42:22:42:24 | toInt(...) |
|
||||
| exprs.kt:43:16:43:24 | ... > ... | exprs.kt:43:16:43:18 | toInt(...) | exprs.kt:43:22:43:24 | toInt(...) |
|
||||
| exprs.kt:44:16:44:25 | ... >= ... | exprs.kt:44:16:44:18 | toInt(...) | exprs.kt:44:23:44:25 | toInt(...) |
|
||||
| exprs.kt:39:15:39:24 | ... (value equals) ... | exprs.kt:39:15:39:17 | intValue(...) | exprs.kt:39:22:39:24 | intValue(...) |
|
||||
| exprs.kt:40:15:40:24 | ... (value not-equals) ... | exprs.kt:40:15:40:17 | intValue(...) | exprs.kt:40:22:40:24 | intValue(...) |
|
||||
| exprs.kt:41:15:41:23 | ... < ... | exprs.kt:41:15:41:17 | intValue(...) | exprs.kt:41:21:41:23 | intValue(...) |
|
||||
| exprs.kt:42:15:42:24 | ... <= ... | exprs.kt:42:15:42:17 | intValue(...) | exprs.kt:42:22:42:24 | intValue(...) |
|
||||
| exprs.kt:43:16:43:24 | ... > ... | exprs.kt:43:16:43:18 | intValue(...) | exprs.kt:43:22:43:24 | intValue(...) |
|
||||
| exprs.kt:44:16:44:25 | ... >= ... | exprs.kt:44:16:44:18 | intValue(...) | exprs.kt:44:23:44:25 | intValue(...) |
|
||||
| exprs.kt:45:16:45:26 | ... == ... | exprs.kt:45:16:45:18 | byx | exprs.kt:45:24:45:26 | byy |
|
||||
| exprs.kt:46:16:46:26 | ... != ... | exprs.kt:46:16:46:18 | byx | exprs.kt:46:24:46:26 | byy |
|
||||
| exprs.kt:49:14:49:20 | ... + ... | exprs.kt:49:14:49:15 | sx | exprs.kt:49:19:49:20 | sy |
|
||||
| exprs.kt:50:14:50:20 | ... - ... | exprs.kt:50:14:50:15 | sx | exprs.kt:50:19:50:20 | sy |
|
||||
| exprs.kt:51:14:51:20 | ... / ... | exprs.kt:51:14:51:15 | sx | exprs.kt:51:19:51:20 | sy |
|
||||
| exprs.kt:52:14:52:20 | ... % ... | exprs.kt:52:14:52:15 | sx | exprs.kt:52:19:52:20 | sy |
|
||||
| exprs.kt:53:14:53:21 | ... (value equals) ... | exprs.kt:53:14:53:15 | toInt(...) | exprs.kt:53:20:53:21 | toInt(...) |
|
||||
| exprs.kt:54:14:54:21 | ... (value not-equals) ... | exprs.kt:54:14:54:15 | toInt(...) | exprs.kt:54:20:54:21 | toInt(...) |
|
||||
| exprs.kt:55:14:55:20 | ... < ... | exprs.kt:55:14:55:15 | toInt(...) | exprs.kt:55:19:55:20 | toInt(...) |
|
||||
| exprs.kt:56:14:56:21 | ... <= ... | exprs.kt:56:14:56:15 | toInt(...) | exprs.kt:56:20:56:21 | toInt(...) |
|
||||
| exprs.kt:57:15:57:21 | ... > ... | exprs.kt:57:15:57:16 | toInt(...) | exprs.kt:57:20:57:21 | toInt(...) |
|
||||
| exprs.kt:58:15:58:22 | ... >= ... | exprs.kt:58:15:58:16 | toInt(...) | exprs.kt:58:21:58:22 | toInt(...) |
|
||||
| exprs.kt:53:14:53:21 | ... (value equals) ... | exprs.kt:53:14:53:15 | intValue(...) | exprs.kt:53:20:53:21 | intValue(...) |
|
||||
| exprs.kt:54:14:54:21 | ... (value not-equals) ... | exprs.kt:54:14:54:15 | intValue(...) | exprs.kt:54:20:54:21 | intValue(...) |
|
||||
| exprs.kt:55:14:55:20 | ... < ... | exprs.kt:55:14:55:15 | intValue(...) | exprs.kt:55:19:55:20 | intValue(...) |
|
||||
| exprs.kt:56:14:56:21 | ... <= ... | exprs.kt:56:14:56:15 | intValue(...) | exprs.kt:56:20:56:21 | intValue(...) |
|
||||
| exprs.kt:57:15:57:21 | ... > ... | exprs.kt:57:15:57:16 | intValue(...) | exprs.kt:57:20:57:21 | intValue(...) |
|
||||
| exprs.kt:58:15:58:22 | ... >= ... | exprs.kt:58:15:58:16 | intValue(...) | exprs.kt:58:21:58:22 | intValue(...) |
|
||||
| exprs.kt:59:15:59:23 | ... == ... | exprs.kt:59:15:59:16 | sx | exprs.kt:59:22:59:23 | sy |
|
||||
| exprs.kt:60:15:60:23 | ... != ... | exprs.kt:60:15:60:16 | sx | exprs.kt:60:22:60:23 | sy |
|
||||
| exprs.kt:63:14:63:20 | ... + ... | exprs.kt:63:14:63:15 | lx | exprs.kt:63:19:63:20 | ly |
|
||||
|
||||
@@ -1014,40 +1014,40 @@
|
||||
| exprs.kt:38:21:38:23 | byy | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:39:5:39:24 | by6 | exprs.kt:4:1:136:1 | topLevelMethod | LocalVariableDeclExpr |
|
||||
| exprs.kt:39:15:39:17 | byx | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:39:15:39:17 | toInt(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:39:15:39:17 | intValue(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:39:15:39:24 | ... (value equals) ... | exprs.kt:4:1:136:1 | topLevelMethod | ValueEQExpr |
|
||||
| exprs.kt:39:22:39:24 | byy | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:39:22:39:24 | toInt(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:39:22:39:24 | intValue(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:40:5:40:24 | by7 | exprs.kt:4:1:136:1 | topLevelMethod | LocalVariableDeclExpr |
|
||||
| exprs.kt:40:15:40:17 | byx | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:40:15:40:17 | toInt(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:40:15:40:17 | intValue(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:40:15:40:24 | ... (value not-equals) ... | exprs.kt:4:1:136:1 | topLevelMethod | ValueNEExpr |
|
||||
| exprs.kt:40:22:40:24 | byy | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:40:22:40:24 | toInt(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:40:22:40:24 | intValue(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:41:5:41:23 | by8 | exprs.kt:4:1:136:1 | topLevelMethod | LocalVariableDeclExpr |
|
||||
| exprs.kt:41:15:41:17 | byx | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:41:15:41:17 | toInt(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:41:15:41:17 | intValue(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:41:15:41:23 | ... < ... | exprs.kt:4:1:136:1 | topLevelMethod | LTExpr |
|
||||
| exprs.kt:41:21:41:23 | byy | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:41:21:41:23 | toInt(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:41:21:41:23 | intValue(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:42:5:42:24 | by9 | exprs.kt:4:1:136:1 | topLevelMethod | LocalVariableDeclExpr |
|
||||
| exprs.kt:42:15:42:17 | byx | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:42:15:42:17 | toInt(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:42:15:42:17 | intValue(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:42:15:42:24 | ... <= ... | exprs.kt:4:1:136:1 | topLevelMethod | LEExpr |
|
||||
| exprs.kt:42:22:42:24 | byy | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:42:22:42:24 | toInt(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:42:22:42:24 | intValue(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:43:5:43:24 | by10 | exprs.kt:4:1:136:1 | topLevelMethod | LocalVariableDeclExpr |
|
||||
| exprs.kt:43:16:43:18 | byx | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:43:16:43:18 | toInt(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:43:16:43:18 | intValue(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:43:16:43:24 | ... > ... | exprs.kt:4:1:136:1 | topLevelMethod | GTExpr |
|
||||
| exprs.kt:43:22:43:24 | byy | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:43:22:43:24 | toInt(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:43:22:43:24 | intValue(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:44:5:44:25 | by11 | exprs.kt:4:1:136:1 | topLevelMethod | LocalVariableDeclExpr |
|
||||
| exprs.kt:44:16:44:18 | byx | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:44:16:44:18 | toInt(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:44:16:44:18 | intValue(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:44:16:44:25 | ... >= ... | exprs.kt:4:1:136:1 | topLevelMethod | GEExpr |
|
||||
| exprs.kt:44:23:44:25 | byy | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:44:23:44:25 | toInt(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:44:23:44:25 | intValue(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:45:5:45:26 | by12 | exprs.kt:4:1:136:1 | topLevelMethod | LocalVariableDeclExpr |
|
||||
| exprs.kt:45:16:45:18 | byx | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:45:16:45:26 | ... == ... | exprs.kt:4:1:136:1 | topLevelMethod | EQExpr |
|
||||
@@ -1075,41 +1075,41 @@
|
||||
| exprs.kt:52:14:52:20 | ... % ... | exprs.kt:4:1:136:1 | topLevelMethod | RemExpr |
|
||||
| exprs.kt:52:19:52:20 | sy | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:53:5:53:21 | s6 | exprs.kt:4:1:136:1 | topLevelMethod | LocalVariableDeclExpr |
|
||||
| exprs.kt:53:14:53:15 | intValue(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:53:14:53:15 | sx | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:53:14:53:15 | toInt(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:53:14:53:21 | ... (value equals) ... | exprs.kt:4:1:136:1 | topLevelMethod | ValueEQExpr |
|
||||
| exprs.kt:53:20:53:21 | intValue(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:53:20:53:21 | sy | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:53:20:53:21 | toInt(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:54:5:54:21 | s7 | exprs.kt:4:1:136:1 | topLevelMethod | LocalVariableDeclExpr |
|
||||
| exprs.kt:54:14:54:15 | intValue(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:54:14:54:15 | sx | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:54:14:54:15 | toInt(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:54:14:54:21 | ... (value not-equals) ... | exprs.kt:4:1:136:1 | topLevelMethod | ValueNEExpr |
|
||||
| exprs.kt:54:20:54:21 | intValue(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:54:20:54:21 | sy | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:54:20:54:21 | toInt(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:55:5:55:20 | s8 | exprs.kt:4:1:136:1 | topLevelMethod | LocalVariableDeclExpr |
|
||||
| exprs.kt:55:14:55:15 | intValue(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:55:14:55:15 | sx | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:55:14:55:15 | toInt(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:55:14:55:20 | ... < ... | exprs.kt:4:1:136:1 | topLevelMethod | LTExpr |
|
||||
| exprs.kt:55:19:55:20 | intValue(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:55:19:55:20 | sy | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:55:19:55:20 | toInt(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:56:5:56:21 | s9 | exprs.kt:4:1:136:1 | topLevelMethod | LocalVariableDeclExpr |
|
||||
| exprs.kt:56:14:56:15 | intValue(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:56:14:56:15 | sx | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:56:14:56:15 | toInt(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:56:14:56:21 | ... <= ... | exprs.kt:4:1:136:1 | topLevelMethod | LEExpr |
|
||||
| exprs.kt:56:20:56:21 | intValue(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:56:20:56:21 | sy | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:56:20:56:21 | toInt(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:57:5:57:21 | s10 | exprs.kt:4:1:136:1 | topLevelMethod | LocalVariableDeclExpr |
|
||||
| exprs.kt:57:15:57:16 | intValue(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:57:15:57:16 | sx | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:57:15:57:16 | toInt(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:57:15:57:21 | ... > ... | exprs.kt:4:1:136:1 | topLevelMethod | GTExpr |
|
||||
| exprs.kt:57:20:57:21 | intValue(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:57:20:57:21 | sy | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:57:20:57:21 | toInt(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:58:5:58:22 | s11 | exprs.kt:4:1:136:1 | topLevelMethod | LocalVariableDeclExpr |
|
||||
| exprs.kt:58:15:58:16 | intValue(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:58:15:58:16 | sx | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:58:15:58:16 | toInt(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:58:15:58:22 | ... >= ... | exprs.kt:4:1:136:1 | topLevelMethod | GEExpr |
|
||||
| exprs.kt:58:21:58:22 | intValue(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:58:21:58:22 | sy | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:58:21:58:22 | toInt(...) | exprs.kt:4:1:136:1 | topLevelMethod | MethodAccess |
|
||||
| exprs.kt:59:5:59:23 | s12 | exprs.kt:4:1:136:1 | topLevelMethod | LocalVariableDeclExpr |
|
||||
| exprs.kt:59:15:59:16 | sx | exprs.kt:4:1:136:1 | topLevelMethod | VarAccess |
|
||||
| exprs.kt:59:15:59:23 | ... == ... | exprs.kt:4:1:136:1 | topLevelMethod | EQExpr |
|
||||
|
||||
@@ -33,7 +33,7 @@ methodWithDuplicate
|
||||
| AbstractList | indexOf | Object |
|
||||
| AbstractList | lastIndexOf | Object |
|
||||
| AbstractList | listIterator | int |
|
||||
| AbstractList | removeAt | int |
|
||||
| AbstractList | remove | int |
|
||||
| AbstractList | removeRange | int |
|
||||
| AbstractList | set | E |
|
||||
| AbstractList | set | int |
|
||||
@@ -48,7 +48,7 @@ methodWithDuplicate
|
||||
| AbstractList<E> | indexOf | Object |
|
||||
| AbstractList<E> | lastIndexOf | Object |
|
||||
| AbstractList<E> | listIterator | int |
|
||||
| AbstractList<E> | removeAt | int |
|
||||
| AbstractList<E> | remove | int |
|
||||
| AbstractList<E> | removeRange | int |
|
||||
| AbstractList<E> | set | E |
|
||||
| AbstractList<E> | set | int |
|
||||
@@ -90,7 +90,7 @@ methodWithDuplicate
|
||||
| AbstractMutableCollection | add | E |
|
||||
| AbstractMutableList | add | E |
|
||||
| AbstractMutableList | add | int |
|
||||
| AbstractMutableList | removeAt | int |
|
||||
| AbstractMutableList | remove | int |
|
||||
| AbstractMutableList | set | E |
|
||||
| AbstractMutableList | set | int |
|
||||
| AbstractMutableMap | put | K |
|
||||
@@ -383,8 +383,8 @@ methodWithDuplicate
|
||||
| MutableList | addAll | int |
|
||||
| MutableList | listIterator | int |
|
||||
| MutableList | remove | Object |
|
||||
| MutableList | remove | int |
|
||||
| MutableList | removeAll | Collection<?> |
|
||||
| MutableList | removeAt | int |
|
||||
| MutableList | replaceAll | UnaryOperator<E> |
|
||||
| MutableList | retainAll | Collection<?> |
|
||||
| MutableList | set | E |
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
public class Test {
|
||||
|
||||
byte b;
|
||||
short s;
|
||||
int i;
|
||||
long l;
|
||||
float f;
|
||||
double d;
|
||||
|
||||
public void test(Number n, Byte b2) {
|
||||
|
||||
b = n.byteValue();
|
||||
s = n.shortValue();
|
||||
i = n.intValue();
|
||||
l = n.longValue();
|
||||
f = n.floatValue();
|
||||
d = n.doubleValue();
|
||||
b = b2.byteValue();
|
||||
s = b2.shortValue();
|
||||
i = b2.intValue();
|
||||
l = b2.longValue();
|
||||
f = b2.floatValue();
|
||||
d = b2.doubleValue();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
| java.lang.Byte | byteValue |
|
||||
| java.lang.Byte | compare |
|
||||
| java.lang.Byte | compareTo |
|
||||
| java.lang.Byte | compareUnsigned |
|
||||
| java.lang.Byte | decode |
|
||||
| java.lang.Byte | describeConstable |
|
||||
| java.lang.Byte | doubleValue |
|
||||
| java.lang.Byte | equals |
|
||||
| java.lang.Byte | floatValue |
|
||||
| java.lang.Byte | hashCode |
|
||||
| java.lang.Byte | intValue |
|
||||
| java.lang.Byte | longValue |
|
||||
| java.lang.Byte | parseByte |
|
||||
| java.lang.Byte | shortValue |
|
||||
| java.lang.Byte | toString |
|
||||
| java.lang.Byte | toUnsignedInt |
|
||||
| java.lang.Byte | toUnsignedLong |
|
||||
| java.lang.Byte | valueOf |
|
||||
| java.lang.Number | byteValue |
|
||||
| java.lang.Number | doubleValue |
|
||||
| java.lang.Number | floatValue |
|
||||
| java.lang.Number | intValue |
|
||||
| java.lang.Number | longValue |
|
||||
| java.lang.Number | shortValue |
|
||||
| kotlin.Byte | byteValue |
|
||||
| kotlin.Byte | compareTo |
|
||||
| kotlin.Byte | dec |
|
||||
| kotlin.Byte | describeConstable |
|
||||
| kotlin.Byte | div |
|
||||
| kotlin.Byte | doubleValue |
|
||||
| kotlin.Byte | floatValue |
|
||||
| kotlin.Byte | inc |
|
||||
| kotlin.Byte | intValue |
|
||||
| kotlin.Byte | longValue |
|
||||
| kotlin.Byte | minus |
|
||||
| kotlin.Byte | plus |
|
||||
| kotlin.Byte | rangeTo |
|
||||
| kotlin.Byte | rem |
|
||||
| kotlin.Byte | shortValue |
|
||||
| kotlin.Byte | times |
|
||||
| kotlin.Byte | toChar |
|
||||
| kotlin.Byte | unaryMinus |
|
||||
| kotlin.Byte | unaryPlus |
|
||||
| kotlin.Number | byteValue |
|
||||
| kotlin.Number | doubleValue |
|
||||
| kotlin.Number | floatValue |
|
||||
| kotlin.Number | intValue |
|
||||
| kotlin.Number | longValue |
|
||||
| kotlin.Number | shortValue |
|
||||
| kotlin.Number | toChar |
|
||||
@@ -0,0 +1 @@
|
||||
fun f(n: Number, b: Byte) = n.toByte() + n.toShort() + n.toInt() + n.toLong() + n.toFloat() + n.toDouble() + b.toByte() + b.toShort() + b.toInt() + b.toLong() + b.toFloat() + b.toDouble()
|
||||
@@ -0,0 +1,5 @@
|
||||
import java
|
||||
|
||||
from Method m
|
||||
where m.getDeclaringType().getName() = ["Number", "Byte"]
|
||||
select m.getDeclaringType().getQualifiedName(), m.toString()
|
||||
@@ -0,0 +1,11 @@
|
||||
import java.util.AbstractList;
|
||||
|
||||
public class MyList<T> extends AbstractList<T> {
|
||||
|
||||
public T get(int idx) { return null; }
|
||||
|
||||
public T remove(int idx) { return null; }
|
||||
|
||||
public int size() { return 0; }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
| get |
|
||||
| remove |
|
||||
| size |
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
fun f(l: MyList<String>) = l.get(0)
|
||||
@@ -0,0 +1,5 @@
|
||||
import java
|
||||
|
||||
from Method m
|
||||
where m.getDeclaringType().getName().matches("MyList%")
|
||||
select m.toString()
|
||||
@@ -0,0 +1,2 @@
|
||||
| test.kt:1:100:1:109 | mutableIterator(...) | mutableIterator |
|
||||
| test.kt:3:73:3:82 | iterator(...) | iterator |
|
||||
@@ -0,0 +1,3 @@
|
||||
fun getIter(m: MutableMap<String, Int>): MutableIterator<MutableMap.MutableEntry<String, Int>> = m.iterator()
|
||||
|
||||
fun getIter2(m: Map<String, Int>): Iterator<Map.Entry<String, Int>> = m.iterator()
|
||||
@@ -0,0 +1,4 @@
|
||||
import java
|
||||
|
||||
from MethodAccess ma
|
||||
select ma, ma.getCallee().toString()
|
||||
@@ -1,18 +1,18 @@
|
||||
| NonSerializableFieldTest.java:25:8:25:19 | problematic1 | This field is in a serializable class, but is not serializable itself because NS is not serializable. |
|
||||
| NonSerializableFieldTest.java:26:14:26:25 | problematic2 | This field is in a serializable class, but is not serializable itself because NS is not serializable. |
|
||||
| NonSerializableFieldTest.java:27:16:27:27 | problematic3 | This field is in a serializable class, but is not serializable itself because ? is not serializable. |
|
||||
| NonSerializableFieldTest.java:27:16:27:27 | problematic3 | This field is in a serializable class, but is not serializable itself because NS is not serializable. |
|
||||
| NonSerializableFieldTest.java:28:16:28:27 | problematic4 | This field is in a serializable class, but is not serializable itself because ? is not serializable. |
|
||||
| NonSerializableFieldTest.java:28:16:28:27 | problematic4 | This field is in a serializable class, but is not serializable itself because NS is not serializable. |
|
||||
| NonSerializableFieldTest.java:29:30:29:41 | problematic5 | This field is in a serializable class, but is not serializable itself because Map<?,NS> is not serializable. |
|
||||
| NonSerializableFieldTest.java:30:9:30:20 | problematic6 | This field is in a serializable class, but is not serializable itself because Map<> is not serializable. |
|
||||
| NonSerializableFieldTest.java:31:24:31:35 | problematic7 | This field is in a serializable class, but is not serializable itself because ? extends NS is not serializable. |
|
||||
| NonSerializableFieldTest.java:32:22:32:33 | problematic8 | This field is in a serializable class, but is not serializable itself because ? super NS is not serializable. |
|
||||
| NonSerializableFieldTest.java:33:7:33:18 | problematic9 | This field is in a serializable class, but is not serializable itself because T is not serializable. |
|
||||
| NonSerializableFieldTest.java:34:13:34:25 | problematic10 | This field is in a serializable class, but is not serializable itself because T is not serializable. |
|
||||
| NonSerializableFieldTest.java:35:13:35:25 | problematic11 | This field is in a serializable class, but is not serializable itself because ? is not serializable. |
|
||||
| NonSerializableFieldTest.java:36:15:36:27 | problematic12 | This field is in a serializable class, but is not serializable itself because ? is not serializable. |
|
||||
| NonSerializableFieldTest.java:37:34:37:46 | problematic13 | This field is in a serializable class, but is not serializable itself because Map<?,Double> is not serializable. |
|
||||
| NonSerializableFieldTest.java:38:21:38:33 | problematic14 | This field is in a serializable class, but is not serializable itself because ? is not serializable. |
|
||||
| NonSerializableFieldTest.java:79:10:79:20 | problematic | This field is in a serializable class, but is not serializable itself because NS is not serializable. |
|
||||
| NonSerializableFieldTest.java:109:26:109:45 | nonSerializableField | This field is in a serializable class, but is not serializable itself because NonSerializableClass is not serializable. |
|
||||
| NonSerializableFieldTest.java:25:8:25:19 | problematic1 | This field is in a serializable class, but is not serializable itself because NS is not serializable. |
|
||||
| NonSerializableFieldTest.java:26:14:26:25 | problematic2 | This field is in a serializable class, but is not serializable itself because NS is not serializable. |
|
||||
| NonSerializableFieldTest.java:27:16:27:27 | problematic3 | This field is in a serializable class, but is not serializable itself because ? is not serializable. |
|
||||
| NonSerializableFieldTest.java:27:16:27:27 | problematic3 | This field is in a serializable class, but is not serializable itself because NS is not serializable. |
|
||||
| NonSerializableFieldTest.java:28:16:28:27 | problematic4 | This field is in a serializable class, but is not serializable itself because ? is not serializable. |
|
||||
| NonSerializableFieldTest.java:28:16:28:27 | problematic4 | This field is in a serializable class, but is not serializable itself because NS is not serializable. |
|
||||
| NonSerializableFieldTest.java:29:30:29:41 | problematic5 | This field is in a serializable class, but is not serializable itself because Map<?,NS> is not serializable. |
|
||||
| NonSerializableFieldTest.java:30:9:30:20 | problematic6 | This field is in a serializable class, but is not serializable itself because Map<> is not serializable. |
|
||||
| NonSerializableFieldTest.java:31:24:31:35 | problematic7 | This field is in a serializable class, but is not serializable itself because ? extends NS is not serializable. |
|
||||
| NonSerializableFieldTest.java:32:22:32:33 | problematic8 | This field is in a serializable class, but is not serializable itself because ? super NS is not serializable. |
|
||||
| NonSerializableFieldTest.java:33:7:33:18 | problematic9 | This field is in a serializable class, but is not serializable itself because T is not serializable. |
|
||||
| NonSerializableFieldTest.java:34:13:34:25 | problematic10 | This field is in a serializable class, but is not serializable itself because T is not serializable. |
|
||||
| NonSerializableFieldTest.java:35:13:35:25 | problematic11 | This field is in a serializable class, but is not serializable itself because ? is not serializable. |
|
||||
| NonSerializableFieldTest.java:36:15:36:27 | problematic12 | This field is in a serializable class, but is not serializable itself because ? is not serializable. |
|
||||
| NonSerializableFieldTest.java:37:34:37:46 | problematic13 | This field is in a serializable class, but is not serializable itself because Map<?,Double> is not serializable. |
|
||||
| NonSerializableFieldTest.java:38:21:38:33 | problematic14 | This field is in a serializable class, but is not serializable itself because ? is not serializable. |
|
||||
| NonSerializableFieldTest.java:79:10:79:20 | problematic | This field is in a serializable class, but is not serializable itself because NS is not serializable. |
|
||||
| NonSerializableFieldTest.java:109:26:109:45 | nonSerializableField | This field is in a serializable class, but is not serializable itself because NonSerializableClass is not serializable. |
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
*/
|
||||
|
||||
import semmle.javascript.dataflow.DataFlow::DataFlow as DataFlow
|
||||
import semmle.javascript.security.CryptoAlgorithms as CryptoAlgorithms
|
||||
|
||||
@@ -11,3 +11,79 @@
|
||||
*/
|
||||
|
||||
private import ConceptsImports
|
||||
|
||||
/**
|
||||
* Provides models for cryptographic concepts.
|
||||
*
|
||||
* Note: The `CryptographicAlgorithm` class currently doesn't take weak keys into
|
||||
* consideration for the `isWeak` member predicate. So RSA is always considered
|
||||
* secure, although using a low number of bits will actually make it insecure. We plan
|
||||
* to improve our libraries in the future to more precisely capture this aspect.
|
||||
*/
|
||||
module Cryptography {
|
||||
class CryptographicAlgorithm = CryptoAlgorithms::CryptographicAlgorithm;
|
||||
|
||||
class EncryptionAlgorithm = CryptoAlgorithms::EncryptionAlgorithm;
|
||||
|
||||
class HashingAlgorithm = CryptoAlgorithms::HashingAlgorithm;
|
||||
|
||||
class PasswordHashingAlgorithm = CryptoAlgorithms::PasswordHashingAlgorithm;
|
||||
|
||||
/**
|
||||
* A data-flow node that is an application of a cryptographic algorithm. For example,
|
||||
* encryption, decryption, signature-validation.
|
||||
*
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `CryptographicOperation::Range` instead.
|
||||
*/
|
||||
class CryptographicOperation extends DataFlow::Node instanceof CryptographicOperation::Range {
|
||||
/** Gets the algorithm used, if it matches a known `CryptographicAlgorithm`. */
|
||||
CryptographicAlgorithm getAlgorithm() { result = super.getAlgorithm() }
|
||||
|
||||
/** Gets an input the algorithm is used on, for example the plain text input to be encrypted. */
|
||||
DataFlow::Node getAnInput() { result = super.getAnInput() }
|
||||
|
||||
/**
|
||||
* Gets the block mode used to perform this cryptographic operation.
|
||||
* This may have no result - for example if the `CryptographicAlgorithm` used
|
||||
* is a stream cipher rather than a block cipher.
|
||||
*/
|
||||
BlockMode getBlockMode() { result = super.getBlockMode() }
|
||||
}
|
||||
|
||||
/** Provides classes for modeling new applications of a cryptographic algorithms. */
|
||||
module CryptographicOperation {
|
||||
/**
|
||||
* A data-flow node that is an application of a cryptographic algorithm. For example,
|
||||
* encryption, decryption, signature-validation.
|
||||
*
|
||||
* Extend this class to model new APIs. If you want to refine existing API models,
|
||||
* extend `CryptographicOperation` instead.
|
||||
*/
|
||||
abstract class Range extends DataFlow::Node {
|
||||
/** Gets the algorithm used, if it matches a known `CryptographicAlgorithm`. */
|
||||
abstract CryptographicAlgorithm getAlgorithm();
|
||||
|
||||
/** Gets an input the algorithm is used on, for example the plain text input to be encrypted. */
|
||||
abstract DataFlow::Node getAnInput();
|
||||
|
||||
/**
|
||||
* Gets the block mode used to perform this cryptographic operation.
|
||||
* This may have no result - for example if the `CryptographicAlgorithm` used
|
||||
* is a stream cipher rather than a block cipher.
|
||||
*/
|
||||
abstract BlockMode getBlockMode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A cryptographic block cipher mode of operation. This can be used to encrypt
|
||||
* data of arbitrary length using a block encryption algorithm.
|
||||
*/
|
||||
class BlockMode extends string {
|
||||
BlockMode() { this = ["ECB", "CBC", "GCM", "CCM", "CFB", "OFB", "CTR", "OPENPGP"] }
|
||||
|
||||
/** Holds if this block mode is considered to be insecure. */
|
||||
predicate isWeak() { this = "ECB" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,9 @@ class EncryptionAlgorithm extends MkEncryptionAlgorithm, CryptographicAlgorithm
|
||||
override string getName() { result = name }
|
||||
|
||||
override predicate isWeak() { isWeak = true }
|
||||
|
||||
/** Holds if this algorithm is a stream cipher. */
|
||||
predicate isStreamCipher() { isStreamCipher(name) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -67,6 +67,6 @@ predicate isStrongPasswordHashingAlgorithm(string name) {
|
||||
predicate isWeakPasswordHashingAlgorithm(string name) { name = "EVPKDF" }
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a weak block cipher mode of operation.
|
||||
* Holds if `name` corresponds to a stream cipher.
|
||||
*/
|
||||
predicate isWeakBlockMode(string name) { name = "ECB" }
|
||||
predicate isStreamCipher(string name) { name = ["CHACHA", "RC4", "ARC4", "ARCFOUR", "RABBIT"] }
|
||||
|
||||
14
javascript/ql/src/meta/alerts/LibraryInputs.ql
Normal file
14
javascript/ql/src/meta/alerts/LibraryInputs.ql
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @name Library inputs
|
||||
* @description An input coming from the client of a library
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @id js/meta/alerts/library-inputs
|
||||
* @tags meta
|
||||
* @precision very-low
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.PackageExports
|
||||
|
||||
select getALibraryInputParameter(), "Library input"
|
||||
@@ -1211,38 +1211,5 @@ module Cryptography {
|
||||
}
|
||||
}
|
||||
|
||||
import semmle.python.concepts.CryptoAlgorithms
|
||||
|
||||
/**
|
||||
* A data-flow node that is an application of a cryptographic algorithm. For example,
|
||||
* encryption, decryption, signature-validation.
|
||||
*
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `CryptographicOperation::Range` instead.
|
||||
*/
|
||||
class CryptographicOperation extends DataFlow::Node instanceof CryptographicOperation::Range {
|
||||
/** Gets the algorithm used, if it matches a known `CryptographicAlgorithm`. */
|
||||
CryptographicAlgorithm getAlgorithm() { result = super.getAlgorithm() }
|
||||
|
||||
/** Gets an input the algorithm is used on, for example the plain text input to be encrypted. */
|
||||
DataFlow::Node getAnInput() { result = super.getAnInput() }
|
||||
}
|
||||
|
||||
/** Provides classes for modeling new applications of a cryptographic algorithms. */
|
||||
module CryptographicOperation {
|
||||
/**
|
||||
* A data-flow node that is an application of a cryptographic algorithm. For example,
|
||||
* encryption, decryption, signature-validation.
|
||||
*
|
||||
* Extend this class to model new APIs. If you want to refine existing API models,
|
||||
* extend `CryptographicOperation` instead.
|
||||
*/
|
||||
abstract class Range extends DataFlow::Node {
|
||||
/** Gets the algorithm used, if it matches a known `CryptographicAlgorithm`. */
|
||||
abstract CryptographicAlgorithm getAlgorithm();
|
||||
|
||||
/** Gets an input the algorithm is used on, for example the plain text input to be encrypted. */
|
||||
abstract DataFlow::Node getAnInput();
|
||||
}
|
||||
}
|
||||
import semmle.python.internal.ConceptsShared::Cryptography
|
||||
}
|
||||
|
||||
@@ -81,6 +81,9 @@ class EncryptionAlgorithm extends MkEncryptionAlgorithm, CryptographicAlgorithm
|
||||
override string getName() { result = name }
|
||||
|
||||
override predicate isWeak() { isWeak = true }
|
||||
|
||||
/** Holds if this algorithm is a stream cipher. */
|
||||
predicate isStreamCipher() { isStreamCipher(name) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -67,6 +67,6 @@ predicate isStrongPasswordHashingAlgorithm(string name) {
|
||||
predicate isWeakPasswordHashingAlgorithm(string name) { name = "EVPKDF" }
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a weak block cipher mode of operation.
|
||||
* Holds if `name` corresponds to a stream cipher.
|
||||
*/
|
||||
predicate isWeakBlockMode(string name) { name = "ECB" }
|
||||
predicate isStreamCipher(string name) { name = ["CHACHA", "RC4", "ARC4", "ARCFOUR", "RABBIT"] }
|
||||
|
||||
@@ -662,7 +662,7 @@ private module AiohttpClientModel {
|
||||
private API::Node instance() { result = classRef().getReturn() }
|
||||
|
||||
/** A method call on a ClientSession that sends off a request */
|
||||
private class OutgoingRequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode {
|
||||
private class OutgoingRequestCall extends HTTP::Client::Request::Range, API::CallNode {
|
||||
string methodName;
|
||||
|
||||
OutgoingRequestCall() {
|
||||
@@ -685,8 +685,14 @@ private module AiohttpClientModel {
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
none()
|
||||
exists(API::Node param | param = this.getKeywordParameter(["ssl", "verify_ssl"]) |
|
||||
disablingNode = param.getARhs() and
|
||||
argumentOrigin = param.getAValueReachingRhs() and
|
||||
// aiohttp.client treats `None` as the default and all other "falsey" values as `False`.
|
||||
argumentOrigin.asExpr().(ImmutableLiteral).booleanValue() = false and
|
||||
not argumentOrigin.asExpr() instanceof None
|
||||
)
|
||||
// TODO: Handling of SSLContext passed as ssl/ssl_context arguments
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,20 +108,20 @@ private module CryptodomeModel {
|
||||
DataFlow::CallCfgNode {
|
||||
string methodName;
|
||||
string cipherName;
|
||||
API::CallNode newCall;
|
||||
|
||||
CryptodomeGenericCipherOperation() {
|
||||
methodName in [
|
||||
"encrypt", "decrypt", "verify", "update", "hexverify", "encrypt_and_digest",
|
||||
"decrypt_and_verify"
|
||||
] and
|
||||
this =
|
||||
newCall =
|
||||
API::moduleImport(["Crypto", "Cryptodome"])
|
||||
.getMember("Cipher")
|
||||
.getMember(cipherName)
|
||||
.getMember("new")
|
||||
.getReturn()
|
||||
.getMember(methodName)
|
||||
.getACall()
|
||||
.getACall() and
|
||||
this = newCall.getReturn().getMember(methodName).getACall()
|
||||
}
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(cipherName) }
|
||||
@@ -155,6 +155,20 @@ private module CryptodomeModel {
|
||||
this.getArgByName("mac_tag")
|
||||
]
|
||||
}
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() {
|
||||
// `modeName` is of the form "MODE_<BlockMode>"
|
||||
exists(string modeName |
|
||||
newCall.getArg(1) =
|
||||
API::moduleImport(["Crypto", "Cryptodome"])
|
||||
.getMember("Cipher")
|
||||
.getMember(cipherName)
|
||||
.getMember(modeName)
|
||||
.getAUse()
|
||||
|
|
||||
result = modeName.splitAt("_", 1)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,6 +206,8 @@ private module CryptodomeModel {
|
||||
result in [this.getArg(1), this.getArgByName("signature")]
|
||||
)
|
||||
}
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -215,5 +231,7 @@ private module CryptodomeModel {
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(hashName) }
|
||||
|
||||
override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("data")] }
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,8 +170,19 @@ private module CryptographyModel {
|
||||
.getMember(algorithmName)
|
||||
}
|
||||
|
||||
/** Gets a reference to a `cryptography.hazmat.primitives.ciphers.modes` Class */
|
||||
API::Node modeClassRef(string modeName) {
|
||||
result =
|
||||
API::moduleImport("cryptography")
|
||||
.getMember("hazmat")
|
||||
.getMember("primitives")
|
||||
.getMember("ciphers")
|
||||
.getMember("modes")
|
||||
.getMember(modeName)
|
||||
}
|
||||
|
||||
/** Gets a reference to a Cipher instance using algorithm with `algorithmName`. */
|
||||
API::Node cipherInstance(string algorithmName) {
|
||||
API::Node cipherInstance(string algorithmName, string modeName) {
|
||||
exists(API::CallNode call | result = call.getReturn() |
|
||||
call =
|
||||
API::moduleImport("cryptography")
|
||||
@@ -182,7 +193,12 @@ private module CryptographyModel {
|
||||
.getACall() and
|
||||
algorithmClassRef(algorithmName).getReturn().getAUse() in [
|
||||
call.getArg(0), call.getArgByName("algorithm")
|
||||
]
|
||||
] and
|
||||
exists(DataFlow::Node modeArg | modeArg in [call.getArg(1), call.getArgByName("mode")] |
|
||||
if modeArg = modeClassRef(_).getReturn().getAUse()
|
||||
then modeArg = modeClassRef(modeName).getReturn().getAUse()
|
||||
else modeName = "<None or unknown>"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -192,10 +208,11 @@ private module CryptographyModel {
|
||||
class CryptographyGenericCipherOperation extends Cryptography::CryptographicOperation::Range,
|
||||
DataFlow::MethodCallNode {
|
||||
string algorithmName;
|
||||
string modeName;
|
||||
|
||||
CryptographyGenericCipherOperation() {
|
||||
this =
|
||||
cipherInstance(algorithmName)
|
||||
cipherInstance(algorithmName, modeName)
|
||||
.getMember(["decryptor", "encryptor"])
|
||||
.getReturn()
|
||||
.getMember(["update", "update_into"])
|
||||
@@ -207,6 +224,8 @@ private module CryptographyModel {
|
||||
}
|
||||
|
||||
override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("data")] }
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { result = modeName }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,6 +276,8 @@ private module CryptographyModel {
|
||||
}
|
||||
|
||||
override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("data")] }
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,12 @@ private import semmle.python.ApiGraphs
|
||||
* - https://www.python-httpx.org/
|
||||
*/
|
||||
private module HttpxModel {
|
||||
private class RequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode {
|
||||
/**
|
||||
* An outgoing HTTP request, from the `httpx` library.
|
||||
*
|
||||
* See https://www.python-httpx.org/api/
|
||||
*/
|
||||
private class RequestCall extends HTTP::Client::Request::Range, API::CallNode {
|
||||
string methodName;
|
||||
|
||||
RequestCall() {
|
||||
@@ -39,15 +44,18 @@ private module HttpxModel {
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
none()
|
||||
disablingNode = this.getKeywordParameter("verify").getARhs() and
|
||||
argumentOrigin = this.getKeywordParameter("verify").getAValueReachingRhs() and
|
||||
// unlike `requests`, httpx treats `None` as turning off verify (and not as the default)
|
||||
argumentOrigin.asExpr().(ImmutableLiteral).booleanValue() = false
|
||||
// TODO: Handling of insecure SSLContext passed to verify argument
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for the `httpx.[Async]Client` class
|
||||
*
|
||||
* See https://www.python-httpx.org/async/
|
||||
* See https://www.python-httpx.org/api/#client
|
||||
*/
|
||||
module Client {
|
||||
/** Get a reference to the `httpx.Client` or `httpx.AsyncClient` class. */
|
||||
@@ -55,16 +63,13 @@ private module HttpxModel {
|
||||
result = API::moduleImport("httpx").getMember(["Client", "AsyncClient"])
|
||||
}
|
||||
|
||||
/** Get a reference to an `httpx.Client` or `httpx.AsyncClient` instance. */
|
||||
private API::Node instance() { result = classRef().getReturn() }
|
||||
|
||||
/** A method call on a Client that sends off a request */
|
||||
private class OutgoingRequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode {
|
||||
string methodName;
|
||||
|
||||
OutgoingRequestCall() {
|
||||
methodName in [HTTP::httpVerbLower(), "request", "stream"] and
|
||||
this = instance().getMember(methodName).getACall()
|
||||
this = classRef().getReturn().getMember(methodName).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAUrlPart() {
|
||||
@@ -80,8 +85,16 @@ private module HttpxModel {
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
none()
|
||||
exists(API::CallNode constructor |
|
||||
constructor = classRef().getACall() and
|
||||
this = constructor.getReturn().getMember(methodName).getACall()
|
||||
|
|
||||
disablingNode = constructor.getKeywordParameter("verify").getARhs() and
|
||||
argumentOrigin = constructor.getKeywordParameter("verify").getAValueReachingRhs() and
|
||||
// unlike `requests`, httpx treats `None` as turning off verify (and not as the default)
|
||||
argumentOrigin.asExpr().(ImmutableLiteral).booleanValue() = false
|
||||
// TODO: Handling of insecure SSLContext passed to verify argument
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,14 @@ private import semmle.python.frameworks.Stdlib
|
||||
*
|
||||
* See
|
||||
* - https://pypi.org/project/requests/
|
||||
* - https://docs.python-requests.org/en/latest/
|
||||
* - https://requests.readthedocs.io/en/latest/
|
||||
*/
|
||||
private module Requests {
|
||||
/**
|
||||
* An outgoing HTTP request, from the `requests` library.
|
||||
*
|
||||
* See https://requests.readthedocs.io/en/latest/api/#requests.request
|
||||
*/
|
||||
private class OutgoingRequestCall extends HTTP::Client::Request::Range, API::CallNode {
|
||||
string methodName;
|
||||
|
||||
@@ -58,6 +63,7 @@ private module Requests {
|
||||
) {
|
||||
disablingNode = this.getKeywordParameter("verify").getARhs() and
|
||||
argumentOrigin = this.getKeywordParameter("verify").getAValueReachingRhs() and
|
||||
// requests treats `None` as the default and all other "falsey" values as `False`.
|
||||
argumentOrigin.asExpr().(ImmutableLiteral).booleanValue() = false and
|
||||
not argumentOrigin.asExpr() instanceof None
|
||||
}
|
||||
@@ -81,7 +87,7 @@ private module Requests {
|
||||
/**
|
||||
* Provides models for the `requests.models.Response` class
|
||||
*
|
||||
* See https://docs.python-requests.org/en/latest/api/#requests.Response.
|
||||
* See https://requests.readthedocs.io/en/latest/api/#requests.Response.
|
||||
*/
|
||||
module Response {
|
||||
/** Gets a reference to the `requests.models.Response` class. */
|
||||
|
||||
@@ -41,6 +41,8 @@ private module Rsa {
|
||||
override DataFlow::Node getAnInput() {
|
||||
result in [this.getArg(0), this.getArgByName("message")]
|
||||
}
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,6 +56,8 @@ private module Rsa {
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.getName() = "RSA" }
|
||||
|
||||
override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("crypto")] }
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,6 +83,8 @@ private module Rsa {
|
||||
override DataFlow::Node getAnInput() {
|
||||
result in [this.getArg(0), this.getArgByName("message")]
|
||||
}
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,6 +106,8 @@ private module Rsa {
|
||||
or
|
||||
result in [this.getArg(1), this.getArgByName("signature")]
|
||||
}
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,6 +130,8 @@ private module Rsa {
|
||||
override DataFlow::Node getAnInput() {
|
||||
result in [this.getArg(0), this.getArgByName("message")]
|
||||
}
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,5 +147,7 @@ private module Rsa {
|
||||
override DataFlow::Node getAnInput() {
|
||||
result in [this.getArg(0), this.getArgByName("hash_value")]
|
||||
}
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2671,6 +2671,8 @@ private module StdlibPrivate {
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(hashName) }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = this.getParameter(1, "data").getARhs() }
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2686,6 +2688,8 @@ private module StdlibPrivate {
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(hashName) }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = this.getArg(0) }
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
|
||||
/** Helper predicate for the `HashLibGenericHashOperation` charpred, to prevent a bad join order. */
|
||||
@@ -2709,6 +2713,8 @@ private module StdlibPrivate {
|
||||
HashlibGenericHashOperation() { hashClass = hashlibMember(hashName) }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(hashName) }
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,7 +42,8 @@ private module Urllib {
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
// cannot enable/disable certificate validation on this object, only when used
|
||||
// with `urlopen`, which is modeled below
|
||||
none()
|
||||
}
|
||||
}
|
||||
@@ -63,7 +64,8 @@ private module Urllib {
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
// will validate certificate by default, see https://github.com/python/cpython/blob/243ed5439c32e8517aa745bc2ca9774d99c99d0f/Lib/http/client.py#L1420-L1421
|
||||
// TODO: Handling of insecure SSLContext passed to context argument
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,8 @@ private module Urllib2 {
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
// cannot enable/disable certificate validation on this object, only when used
|
||||
// with `urlopen`, which is modeled below
|
||||
none()
|
||||
}
|
||||
}
|
||||
@@ -49,7 +50,8 @@ private module Urllib2 {
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
// will validate certificate by default
|
||||
// TODO: Handling of insecure SSLContext passed to context argument
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,9 +42,6 @@ private module Urllib3 {
|
||||
.getASubclass+()
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of a `urllib3.request.RequestMethods` subclass. */
|
||||
private API::Node instance() { result = classRef().getReturn() }
|
||||
|
||||
/**
|
||||
* A call to a method making an outgoing request.
|
||||
*
|
||||
@@ -52,10 +49,11 @@ private module Urllib3 {
|
||||
* - https://urllib3.readthedocs.io/en/stable/reference/urllib3.request.html#urllib3.request.RequestMethods
|
||||
* - https://urllib3.readthedocs.io/en/stable/reference/urllib3.connectionpool.html#urllib3.HTTPConnectionPool.urlopen
|
||||
*/
|
||||
private class RequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode {
|
||||
private class RequestCall extends HTTP::Client::Request::Range, API::CallNode {
|
||||
RequestCall() {
|
||||
this =
|
||||
instance()
|
||||
classRef()
|
||||
.getReturn()
|
||||
.getMember(["request", "request_encode_url", "request_encode_body", "urlopen"])
|
||||
.getACall()
|
||||
}
|
||||
@@ -67,8 +65,22 @@ private module Urllib3 {
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
none()
|
||||
exists(API::CallNode constructor |
|
||||
constructor = classRef().getACall() and
|
||||
this = constructor.getReturn().getAMember().getACall()
|
||||
|
|
||||
// cert_reqs
|
||||
// see https://urllib3.readthedocs.io/en/stable/user-guide.html?highlight=cert_reqs#certificate-verification
|
||||
disablingNode = constructor.getKeywordParameter("cert_reqs").getARhs() and
|
||||
argumentOrigin = constructor.getKeywordParameter("cert_reqs").getAValueReachingRhs() and
|
||||
argumentOrigin.asExpr().(StrConst).getText() = "CERT_NONE"
|
||||
or
|
||||
// assert_hostname
|
||||
// see https://urllib3.readthedocs.io/en/stable/reference/urllib3.connectionpool.html?highlight=assert_hostname#urllib3.HTTPSConnectionPool
|
||||
disablingNode = constructor.getKeywordParameter("assert_hostname").getARhs() and
|
||||
argumentOrigin = constructor.getKeywordParameter("assert_hostname").getAValueReachingRhs() and
|
||||
argumentOrigin.asExpr().(BooleanLiteral).booleanValue() = false
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
*/
|
||||
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.concepts.CryptoAlgorithms as CryptoAlgorithms
|
||||
|
||||
@@ -11,3 +11,79 @@
|
||||
*/
|
||||
|
||||
private import ConceptsImports
|
||||
|
||||
/**
|
||||
* Provides models for cryptographic concepts.
|
||||
*
|
||||
* Note: The `CryptographicAlgorithm` class currently doesn't take weak keys into
|
||||
* consideration for the `isWeak` member predicate. So RSA is always considered
|
||||
* secure, although using a low number of bits will actually make it insecure. We plan
|
||||
* to improve our libraries in the future to more precisely capture this aspect.
|
||||
*/
|
||||
module Cryptography {
|
||||
class CryptographicAlgorithm = CryptoAlgorithms::CryptographicAlgorithm;
|
||||
|
||||
class EncryptionAlgorithm = CryptoAlgorithms::EncryptionAlgorithm;
|
||||
|
||||
class HashingAlgorithm = CryptoAlgorithms::HashingAlgorithm;
|
||||
|
||||
class PasswordHashingAlgorithm = CryptoAlgorithms::PasswordHashingAlgorithm;
|
||||
|
||||
/**
|
||||
* A data-flow node that is an application of a cryptographic algorithm. For example,
|
||||
* encryption, decryption, signature-validation.
|
||||
*
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `CryptographicOperation::Range` instead.
|
||||
*/
|
||||
class CryptographicOperation extends DataFlow::Node instanceof CryptographicOperation::Range {
|
||||
/** Gets the algorithm used, if it matches a known `CryptographicAlgorithm`. */
|
||||
CryptographicAlgorithm getAlgorithm() { result = super.getAlgorithm() }
|
||||
|
||||
/** Gets an input the algorithm is used on, for example the plain text input to be encrypted. */
|
||||
DataFlow::Node getAnInput() { result = super.getAnInput() }
|
||||
|
||||
/**
|
||||
* Gets the block mode used to perform this cryptographic operation.
|
||||
* This may have no result - for example if the `CryptographicAlgorithm` used
|
||||
* is a stream cipher rather than a block cipher.
|
||||
*/
|
||||
BlockMode getBlockMode() { result = super.getBlockMode() }
|
||||
}
|
||||
|
||||
/** Provides classes for modeling new applications of a cryptographic algorithms. */
|
||||
module CryptographicOperation {
|
||||
/**
|
||||
* A data-flow node that is an application of a cryptographic algorithm. For example,
|
||||
* encryption, decryption, signature-validation.
|
||||
*
|
||||
* Extend this class to model new APIs. If you want to refine existing API models,
|
||||
* extend `CryptographicOperation` instead.
|
||||
*/
|
||||
abstract class Range extends DataFlow::Node {
|
||||
/** Gets the algorithm used, if it matches a known `CryptographicAlgorithm`. */
|
||||
abstract CryptographicAlgorithm getAlgorithm();
|
||||
|
||||
/** Gets an input the algorithm is used on, for example the plain text input to be encrypted. */
|
||||
abstract DataFlow::Node getAnInput();
|
||||
|
||||
/**
|
||||
* Gets the block mode used to perform this cryptographic operation.
|
||||
* This may have no result - for example if the `CryptographicAlgorithm` used
|
||||
* is a stream cipher rather than a block cipher.
|
||||
*/
|
||||
abstract BlockMode getBlockMode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A cryptographic block cipher mode of operation. This can be used to encrypt
|
||||
* data of arbitrary length using a block encryption algorithm.
|
||||
*/
|
||||
class BlockMode extends string {
|
||||
BlockMode() { this = ["ECB", "CBC", "GCM", "CCM", "CFB", "OFB", "CTR", "OPENPGP"] }
|
||||
|
||||
/** Holds if this block mode is considered to be insecure. */
|
||||
predicate isWeak() { this = "ECB" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,14 @@ private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
from API::CallNode call, DataFlow::Node falseyOrigin, string verb
|
||||
from
|
||||
HTTP::Client::Request request, DataFlow::Node disablingNode, DataFlow::Node origin, string ending
|
||||
where
|
||||
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
|
||||
select call, "Call to requests." + verb + " with verify=$@", falseyOrigin, "False"
|
||||
request.disablesCertificateValidation(disablingNode, origin) and
|
||||
// Showing the origin is only useful when it's a different node than the one disabling
|
||||
// certificate validation, for example in `requests.get(..., verify=arg)`, `arg` would
|
||||
// be the `disablingNode`, and the `origin` would be the place were `arg` got its
|
||||
// value from.
|
||||
if disablingNode = origin then ending = "." else ending = " by the value from $@."
|
||||
select request, "This request may run without certificate validation because it is $@" + ending,
|
||||
disablingNode, "disabled here", origin, "here"
|
||||
|
||||
@@ -13,13 +13,18 @@
|
||||
import python
|
||||
import semmle.python.Concepts
|
||||
|
||||
from Cryptography::CryptographicOperation operation, Cryptography::CryptographicAlgorithm algorithm
|
||||
from
|
||||
Cryptography::CryptographicOperation operation, Cryptography::CryptographicAlgorithm algorithm,
|
||||
string msgPrefix
|
||||
where
|
||||
algorithm = operation.getAlgorithm() and
|
||||
algorithm.isWeak() and
|
||||
// `Cryptography::HashingAlgorithm` and `Cryptography::PasswordHashingAlgorithm` are
|
||||
// handled by `py/weak-sensitive-data-hashing`
|
||||
algorithm instanceof Cryptography::EncryptionAlgorithm
|
||||
select operation,
|
||||
"The cryptographic algorithm " + algorithm.getName() +
|
||||
" is broken or weak, and should not be used."
|
||||
algorithm instanceof Cryptography::EncryptionAlgorithm and
|
||||
(
|
||||
algorithm.isWeak() and
|
||||
msgPrefix = "The cryptographic algorithm " + operation.getAlgorithm().getName()
|
||||
)
|
||||
or
|
||||
operation.getBlockMode().isWeak() and msgPrefix = "The block mode " + operation.getBlockMode()
|
||||
select operation, msgPrefix + " is broken or weak, and should not be used."
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.ApiGraphs
|
||||
|
||||
bindingset[p]
|
||||
int world_permission(int p) { result = p % 8 }
|
||||
@@ -33,20 +34,20 @@ string permissive_permission(int p) {
|
||||
world_permission(p) = 0 and result = "group " + access(group_permission(p))
|
||||
}
|
||||
|
||||
predicate chmod_call(CallNode call, FunctionValue chmod, NumericValue num) {
|
||||
Value::named("os.chmod") = chmod and
|
||||
chmod.getACall() = call and
|
||||
call.getArg(1).pointsTo(num)
|
||||
predicate chmod_call(API::CallNode call, string name, int mode) {
|
||||
call = API::moduleImport("os").getMember("chmod").getACall() and
|
||||
mode = call.getParameter(1, "mode").getAValueReachingRhs().asExpr().(IntegerLiteral).getValue() and
|
||||
name = "chmod"
|
||||
}
|
||||
|
||||
predicate open_call(CallNode call, FunctionValue open, NumericValue num) {
|
||||
Value::named("os.open") = open and
|
||||
open.getACall() = call and
|
||||
call.getArg(2).pointsTo(num)
|
||||
predicate open_call(API::CallNode call, string name, int mode) {
|
||||
call = API::moduleImport("os").getMember("open").getACall() and
|
||||
mode = call.getParameter(2, "mode").getAValueReachingRhs().asExpr().(IntegerLiteral).getValue() and
|
||||
name = "open"
|
||||
}
|
||||
|
||||
from CallNode call, FunctionValue func, NumericValue num, string permission
|
||||
from API::CallNode call, string name, int mode, string permission
|
||||
where
|
||||
(chmod_call(call, func, num) or open_call(call, func, num)) and
|
||||
permission = permissive_permission(num.getIntValue())
|
||||
select call, "Overly permissive mask in " + func.getName() + " sets file to " + permission + "."
|
||||
(chmod_call(call, name, mode) or open_call(call, name, mode)) and
|
||||
permission = permissive_permission(mode)
|
||||
select call, "Overly permissive mask in " + name + " sets file to " + permission + "."
|
||||
|
||||
@@ -72,7 +72,7 @@ class LgtmSuppressionComment extends LineSuppressionComment {
|
||||
* A noqa suppression comment. Both pylint and pyflakes respect this, so lgtm ought to too.
|
||||
*/
|
||||
class NoqaSuppressionComment extends LineSuppressionComment {
|
||||
NoqaSuppressionComment() { this.getContents().toLowerCase().regexpMatch("\\s*noqa\\s*") }
|
||||
NoqaSuppressionComment() { this.getContents().toLowerCase().regexpMatch("\\s*noqa\\s*([^:].*)?") }
|
||||
|
||||
override string getAnnotation() { result = "lgtm" }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The query "Use of a broken or weak cryptographic algorithm" (`py/weak-cryptographic-algorithm`) now report if a cryptographic operation is potentially insecure due to use of a weak block mode.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: majorAnalysis
|
||||
---
|
||||
* Improved library modeling for the query "Request without certificate validation" (`py/request-without-cert-validation`), so it now also covers `httpx`, `aiohttp.client`, and `urllib3`.
|
||||
23
python/ql/src/experimental/Security/CWE-079/ReflectedXSS.ql
Normal file
23
python/ql/src/experimental/Security/CWE-079/ReflectedXSS.ql
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* @name Reflected server-side cross-site scripting
|
||||
* @description Writing user input directly to a web page
|
||||
* allows for a cross-site scripting vulnerability.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 2.9
|
||||
* @sub-severity high
|
||||
* @id py/reflective-xss
|
||||
* @tags security
|
||||
* external/cwe/cwe-079
|
||||
* external/cwe/cwe-116
|
||||
*/
|
||||
|
||||
// determine precision above
|
||||
import python
|
||||
import experimental.semmle.python.security.dataflow.ReflectedXSS
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from ReflectedXssConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where config.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Cross-site scripting vulnerability due to $@.",
|
||||
source.getNode(), "a user-provided value"
|
||||
@@ -602,3 +602,77 @@ class JwtDecoding extends DataFlow::Node instanceof JwtDecoding::Range {
|
||||
|
||||
/** DEPRECATED: Alias for JwtDecoding */
|
||||
deprecated class JWTDecoding = JwtDecoding;
|
||||
|
||||
/** Provides classes for modeling Email APIs. */
|
||||
module EmailSender {
|
||||
/**
|
||||
* A data-flow node that sends an email.
|
||||
*
|
||||
* Extend this class to model new APIs. If you want to refine existing API models,
|
||||
* extend `EmailSender` instead.
|
||||
*/
|
||||
abstract class Range extends DataFlow::Node {
|
||||
/**
|
||||
* Gets a data flow node holding the plaintext version of the email body.
|
||||
*/
|
||||
abstract DataFlow::Node getPlainTextBody();
|
||||
|
||||
/**
|
||||
* Gets a data flow node holding the html version of the email body.
|
||||
*/
|
||||
abstract DataFlow::Node getHtmlBody();
|
||||
|
||||
/**
|
||||
* Gets a data flow node holding the recipients of the email.
|
||||
*/
|
||||
abstract DataFlow::Node getTo();
|
||||
|
||||
/**
|
||||
* Gets a data flow node holding the senders of the email.
|
||||
*/
|
||||
abstract DataFlow::Node getFrom();
|
||||
|
||||
/**
|
||||
* Gets a data flow node holding the subject of the email.
|
||||
*/
|
||||
abstract DataFlow::Node getSubject();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data-flow node that sends an email.
|
||||
*
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `EmailSender::Range` instead.
|
||||
*/
|
||||
class EmailSender extends DataFlow::Node instanceof EmailSender::Range {
|
||||
/**
|
||||
* Gets a data flow node holding the plaintext version of the email body.
|
||||
*/
|
||||
DataFlow::Node getPlainTextBody() { result = super.getPlainTextBody() }
|
||||
|
||||
/**
|
||||
* Gets a data flow node holding the html version of the email body.
|
||||
*/
|
||||
DataFlow::Node getHtmlBody() { result = super.getHtmlBody() }
|
||||
|
||||
/**
|
||||
* Gets a data flow node holding the recipients of the email.
|
||||
*/
|
||||
DataFlow::Node getTo() { result = super.getTo() }
|
||||
|
||||
/**
|
||||
* Gets a data flow node holding the senders of the email.
|
||||
*/
|
||||
DataFlow::Node getFrom() { result = super.getFrom() }
|
||||
|
||||
/**
|
||||
* Gets a data flow node holding the subject of the email.
|
||||
*/
|
||||
DataFlow::Node getSubject() { result = super.getSubject() }
|
||||
|
||||
/**
|
||||
* Gets a data flow node that refers to the HTML body or plaintext body of the email.
|
||||
*/
|
||||
DataFlow::Node getABody() { result in [super.getPlainTextBody(), super.getHtmlBody()] }
|
||||
}
|
||||
|
||||
@@ -15,3 +15,6 @@ private import experimental.semmle.python.libraries.Python_JWT
|
||||
private import experimental.semmle.python.libraries.Authlib
|
||||
private import experimental.semmle.python.libraries.PythonJose
|
||||
private import experimental.semmle.python.frameworks.CopyFile
|
||||
private import experimental.semmle.python.frameworks.Sendgrid
|
||||
private import experimental.semmle.python.libraries.FlaskMail
|
||||
private import experimental.semmle.python.libraries.SmtpLib
|
||||
|
||||
@@ -8,8 +8,8 @@ private import semmle.python.frameworks.Django
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import experimental.semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.Concepts
|
||||
import semmle.python.dataflow.new.RemoteFlowSources
|
||||
|
||||
private module ExperimentalPrivateDjango {
|
||||
private module DjangoMod {
|
||||
@@ -189,5 +189,90 @@ private module ExperimentalPrivateDjango {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module Email {
|
||||
/** https://docs.djangoproject.com/en/3.2/topics/email/ */
|
||||
private API::Node djangoMail() {
|
||||
result = API::moduleImport("django").getMember("core").getMember("mail")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a call to `django.core.mail.send_mail()`.
|
||||
*
|
||||
* Given the following example:
|
||||
*
|
||||
* ```py
|
||||
* send_mail("Subject", "plain-text body", "from@example.com", ["to@example.com"], html_message=django.http.request.GET.get("html"))
|
||||
* ```
|
||||
*
|
||||
* * `this` would be `send_mail("Subject", "plain-text body", "from@example.com", ["to@example.com"], html_message=django.http.request.GET.get("html"))`.
|
||||
* * `getPlainTextBody()`'s result would be `"plain-text body"`.
|
||||
* * `getHtmlBody()`'s result would be `django.http.request.GET.get("html")`.
|
||||
* * `getTo()`'s result would be `["to@example.com"]`.
|
||||
* * `getFrom()`'s result would be `"from@example.com"`.
|
||||
* * `getSubject()`'s result would be `"Subject"`.
|
||||
*/
|
||||
private class DjangoSendMail extends DataFlow::CallCfgNode, EmailSender::Range {
|
||||
DjangoSendMail() { this = djangoMail().getMember("send_mail").getACall() }
|
||||
|
||||
override DataFlow::Node getPlainTextBody() {
|
||||
result in [this.getArg(1), this.getArgByName("message")]
|
||||
}
|
||||
|
||||
override DataFlow::Node getHtmlBody() {
|
||||
result in [this.getArg(8), this.getArgByName("html_message")]
|
||||
}
|
||||
|
||||
override DataFlow::Node getTo() {
|
||||
result in [this.getArg(3), this.getArgByName("recipient_list")]
|
||||
}
|
||||
|
||||
override DataFlow::Node getFrom() {
|
||||
result in [this.getArg(2), this.getArgByName("from_email")]
|
||||
}
|
||||
|
||||
override DataFlow::Node getSubject() {
|
||||
result in [this.getArg(0), this.getArgByName("subject")]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a call to `django.core.mail.mail_admins()` or `django.core.mail.mail_managers()`.
|
||||
*
|
||||
* Given the following example:
|
||||
*
|
||||
* ```py
|
||||
* mail_admins("Subject", "plain-text body", html_message=django.http.request.GET.get("html"))
|
||||
* ```
|
||||
*
|
||||
* * `this` would be `mail_admins("Subject", "plain-text body", html_message=django.http.request.GET.get("html"))`.
|
||||
* * `getPlainTextBody()`'s result would be `"plain-text body"`.
|
||||
* * `getHtmlBody()`'s result would be `django.http.request.GET.get("html")`.
|
||||
* * `getTo()`'s result would be `none`.
|
||||
* * `getFrom()`'s result would be `none`.
|
||||
* * `getSubject()`'s result would be `"Subject"`.
|
||||
*/
|
||||
private class DjangoMailInternal extends DataFlow::CallCfgNode, EmailSender::Range {
|
||||
DjangoMailInternal() {
|
||||
this = djangoMail().getMember(["mail_admins", "mail_managers"]).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getPlainTextBody() {
|
||||
result in [this.getArg(1), this.getArgByName("message")]
|
||||
}
|
||||
|
||||
override DataFlow::Node getHtmlBody() {
|
||||
result in [this.getArg(4), this.getArgByName("html_message")]
|
||||
}
|
||||
|
||||
override DataFlow::Node getTo() { none() }
|
||||
|
||||
override DataFlow::Node getFrom() { none() }
|
||||
|
||||
override DataFlow::Node getSubject() {
|
||||
result in [this.getArg(0), this.getArgByName("subject")]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
187
python/ql/src/experimental/semmle/python/frameworks/Sendgrid.qll
Normal file
187
python/ql/src/experimental/semmle/python/frameworks/Sendgrid.qll
Normal file
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `sendgrid` PyPI package.
|
||||
* See https://github.com/sendgrid/sendgrid-python.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import experimental.semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
private module Sendgrid {
|
||||
/** Gets a reference to the `sendgrid` module. */
|
||||
private API::Node sendgrid() { result = API::moduleImport("sendgrid") }
|
||||
|
||||
/** Gets a reference to `sendgrid.helpers.mail` */
|
||||
private API::Node sendgridMailHelper() {
|
||||
result = sendgrid().getMember("helpers").getMember("mail")
|
||||
}
|
||||
|
||||
/** Gets a reference to `sendgrid.helpers.mail.Mail` */
|
||||
private API::Node sendgridMailInstance() { result = sendgridMailHelper().getMember("Mail") }
|
||||
|
||||
/** Gets a reference to a `SendGridAPIClient` instance. */
|
||||
private API::Node sendgridApiClient() {
|
||||
result = sendgrid().getMember("SendGridAPIClient").getReturn()
|
||||
}
|
||||
|
||||
/** Gets a reference to a `SendGridAPIClient` instance call with `send` or `post`. */
|
||||
private DataFlow::CallCfgNode sendgridApiSendCall() {
|
||||
result = sendgridApiClient().getMember("send").getACall()
|
||||
or
|
||||
result =
|
||||
sendgridApiClient()
|
||||
.getMember("client")
|
||||
.getMember("mail")
|
||||
.getMember("send")
|
||||
.getMember("post")
|
||||
.getACall()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to `sg.send()` and `sg.client.mail.send.post()`.
|
||||
*
|
||||
* Given the following example:
|
||||
*
|
||||
* ```py
|
||||
* from_email = Email("from@example.com")
|
||||
* to_email = To("to@example.com")
|
||||
* subject = "Sending with SendGrid is Fun"
|
||||
* content = Content("text/html", request.args["html_content"])
|
||||
*
|
||||
* mail = Mail(from_email, to_email, subject, content)
|
||||
*
|
||||
* sg = SendGridAPIClient(api_key='SENDGRID_API_KEY')
|
||||
* response = sg.client.mail.send.post(request_body=mail.get())
|
||||
* ```
|
||||
*
|
||||
* * `this` would be `sg.client.mail.send.post(request_body=mail.get())`.
|
||||
* * `getPlainTextBody()`'s result would be `none()`.
|
||||
* * `getHtmlBody()`'s result would be `request.args["html_content"]`.
|
||||
* * `getTo()`'s result would be `"to@example.com"`.
|
||||
* * `getFrom()`'s result would be `"from@example.com"`.
|
||||
* * `getSubject()`'s result would be `"Sending with SendGrid is Fun"`.
|
||||
*/
|
||||
private class SendGridMail extends DataFlow::CallCfgNode, EmailSender::Range {
|
||||
SendGridMail() { this = sendgridApiSendCall() }
|
||||
|
||||
private DataFlow::CallCfgNode getMailCall() {
|
||||
exists(DataFlow::Node n |
|
||||
n in [this.getArg(0), this.getArgByName("request_body")] and
|
||||
result = [n, n.(DataFlow::MethodCallNode).getObject()].getALocalSource()
|
||||
)
|
||||
}
|
||||
|
||||
private DataFlow::Node sendgridContent(DataFlow::CallCfgNode contentCall, string mime) {
|
||||
mime in ["text/plain", "text/html", "text/x-amp-html"] and
|
||||
exists(StrConst mimeNode |
|
||||
mimeNode.getText() = mime and
|
||||
DataFlow::exprNode(mimeNode).(DataFlow::LocalSourceNode).flowsTo(contentCall.getArg(0)) and
|
||||
result = contentCall.getArg(1)
|
||||
)
|
||||
}
|
||||
|
||||
private DataFlow::Node sendgridWrite(string attributeName) {
|
||||
attributeName in ["plain_text_content", "html_content", "from_email", "subject"] and
|
||||
exists(DataFlow::AttrWrite attrWrite |
|
||||
attrWrite.getObject().getALocalSource() = this.getMailCall() and
|
||||
attrWrite.getAttributeName() = attributeName and
|
||||
result = attrWrite.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getPlainTextBody() {
|
||||
result in [
|
||||
this.getMailCall().getArg(3), this.getMailCall().getArgByName("plain_text_content")
|
||||
]
|
||||
or
|
||||
result in [
|
||||
this.sendgridContent([
|
||||
this.getMailCall().getArg(3), this.getMailCall().getArgByName("plain_text_content")
|
||||
].getALocalSource(), "text/plain"),
|
||||
this.sendgridContent(sendgridMailInstance().getMember("add_content").getACall(),
|
||||
"text/plain")
|
||||
]
|
||||
or
|
||||
result = this.sendgridWrite("plain_text_content")
|
||||
}
|
||||
|
||||
override DataFlow::Node getHtmlBody() {
|
||||
result in [this.getMailCall().getArg(4), this.getMailCall().getArgByName("html_content")]
|
||||
or
|
||||
result = this.getMailCall().getAMethodCall("set_html").getArg(0)
|
||||
or
|
||||
result =
|
||||
this.sendgridContent([
|
||||
this.getMailCall().getArg(4), this.getMailCall().getArgByName("html_content")
|
||||
].getALocalSource(), ["text/html", "text/x-amp-html"])
|
||||
or
|
||||
result = this.sendgridWrite("html_content")
|
||||
or
|
||||
exists(KeyValuePair content, Dict generalDict, KeyValuePair typePair, KeyValuePair valuePair |
|
||||
content.getKey().(StrConst).getText() = "content" and
|
||||
content.getValue().(List).getAnElt() = generalDict and
|
||||
// declare KeyValuePairs keys and values
|
||||
typePair.getKey().(StrConst).getText() = "type" and
|
||||
typePair.getValue().(StrConst).getText() = ["text/html", "text/x-amp-html"] and
|
||||
valuePair.getKey().(StrConst).getText() = "value" and
|
||||
result.asExpr() = valuePair.getValue() and
|
||||
// correlate generalDict with previously set KeyValuePairs
|
||||
generalDict.getAnItem() in [typePair, valuePair] and
|
||||
[this.getArg(0), this.getArgByName("request_body")].getALocalSource().asExpr() =
|
||||
any(Dict d | d.getAnItem() = content)
|
||||
)
|
||||
or
|
||||
exists(KeyValuePair footer, Dict generalDict, KeyValuePair enablePair, KeyValuePair htmlPair |
|
||||
footer.getKey().(StrConst).getText() = ["footer", "subscription_tracking"] and
|
||||
footer.getValue() = generalDict and
|
||||
// check footer is enabled
|
||||
enablePair.getKey().(StrConst).getText() = "enable" and
|
||||
exists(enablePair.getValue().(True)) and
|
||||
// get html content
|
||||
htmlPair.getKey().(StrConst).getText() = "html" and
|
||||
result.asExpr() = htmlPair.getValue() and
|
||||
// correlate generalDict with previously set KeyValuePairs
|
||||
generalDict.getAnItem() in [enablePair, htmlPair] and
|
||||
exists(KeyValuePair k |
|
||||
k.getKey() =
|
||||
[this.getArg(0), this.getArgByName("request_body")]
|
||||
.getALocalSource()
|
||||
.asExpr()
|
||||
.(Dict)
|
||||
.getAKey() and
|
||||
k.getValue() = any(Dict d | d.getAKey() = footer.getKey())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getTo() {
|
||||
result in [this.getMailCall().getArg(1), this.getMailCall().getArgByName("to_emails")]
|
||||
or
|
||||
result = this.getMailCall().getAMethodCall("To").getArg(0)
|
||||
or
|
||||
result =
|
||||
this.getMailCall()
|
||||
.getAMethodCall(["to", "add_to", "cc", "add_cc", "bcc", "add_bcc"])
|
||||
.getArg(0)
|
||||
}
|
||||
|
||||
override DataFlow::Node getFrom() {
|
||||
result in [this.getMailCall().getArg(0), this.getMailCall().getArgByName("from_email")]
|
||||
or
|
||||
result = this.getMailCall().getAMethodCall("Email").getArg(0)
|
||||
or
|
||||
result = this.getMailCall().getAMethodCall(["from_email", "set_from"]).getArg(0)
|
||||
or
|
||||
result = this.sendgridWrite("from_email")
|
||||
}
|
||||
|
||||
override DataFlow::Node getSubject() {
|
||||
result in [this.getMailCall().getArg(2), this.getMailCall().getArgByName("subject")]
|
||||
or
|
||||
result = this.getMailCall().getAMethodCall(["subject", "set_subject"]).getArg(0)
|
||||
or
|
||||
result = this.sendgridWrite("subject")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `flask` PyPI package.
|
||||
* See https://flask.palletsprojects.com/en/1.1.x/.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import experimental.semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/** https://pythonhosted.org/Flask-Mail/#module-flask_mail */
|
||||
private module FlaskMail {
|
||||
/** Gets a reference to `flask_mail`, `flask_sendmail` and `flask.ext.sendmail`. */
|
||||
private API::Node flaskMail() {
|
||||
result = API::moduleImport(["flask_mail", "flask_sendmail", "flask.ext.sendmail"])
|
||||
}
|
||||
|
||||
/** Gets a reference to `flask_mail.Mail()`, `flask_sendmail.Mail()` and `flask.ext.sendmail.Mail()`. */
|
||||
private API::Node flaskMailInstance() { result = flaskMail().getMember("Mail").getReturn() }
|
||||
|
||||
/**
|
||||
* Gets a call to `mail.send()`.
|
||||
*
|
||||
* Given the following example:
|
||||
*
|
||||
* ```py
|
||||
* msg = Message(subject="Subject",
|
||||
* sender="from@example.com",
|
||||
* recipients=["to@example.com"],
|
||||
* body="plain-text body",
|
||||
* html=request.args["html"])
|
||||
* mail.send(msg)
|
||||
* ```
|
||||
*
|
||||
* * `this` would be `mail.send(msg)`.
|
||||
* * `getPlainTextBody()`'s result would be `"plain-text body"`.
|
||||
* * `getHtmlBody()`'s result would be `request.args["html"]`.
|
||||
* * `getTo()`'s result would be `["to@example.com"]`.
|
||||
* * `getFrom()`'s result would be `"from@example.com"`.
|
||||
* * `getSubject()`'s result would be `"Subject"`.
|
||||
*/
|
||||
private class FlaskMail extends DataFlow::CallCfgNode, EmailSender::Range {
|
||||
FlaskMail() {
|
||||
this =
|
||||
[flaskMailInstance(), flaskMailInstance().getMember("connect").getReturn()]
|
||||
.getMember(["send", "send_message"])
|
||||
.getACall()
|
||||
}
|
||||
|
||||
private DataFlow::CallCfgNode getMessage() { result = this.getArg(0).getALocalSource() }
|
||||
|
||||
bindingset[argumentPosition]
|
||||
private DataFlow::Node getFlaskMailArgument(int argumentPosition, string argumentName) {
|
||||
argumentPosition in [[0 .. 3], 5] and
|
||||
argumentName in ["body", "html", "recipients", "sender", "subject"] and
|
||||
result in [
|
||||
this.getMessage().getArg(argumentPosition), this.getMessage().getArgByName(argumentName)
|
||||
]
|
||||
or
|
||||
exists(DataFlow::AttrWrite write |
|
||||
write.getObject().getALocalSource() = this.getMessage() and
|
||||
write.getAttributeName() = argumentName and
|
||||
result = write.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getPlainTextBody() { result = this.getFlaskMailArgument(2, "body") }
|
||||
|
||||
override DataFlow::Node getHtmlBody() { result = this.getFlaskMailArgument(3, "html") }
|
||||
|
||||
override DataFlow::Node getTo() {
|
||||
result = this.getFlaskMailArgument(1, "recipients")
|
||||
or
|
||||
result = this.getMessage().getAMethodCall("add_recipient").getACall().getArg(0)
|
||||
}
|
||||
|
||||
override DataFlow::Node getFrom() { result = this.getFlaskMailArgument(5, "sender") }
|
||||
|
||||
override DataFlow::Node getSubject() { result = this.getFlaskMailArgument(0, "subject") }
|
||||
}
|
||||
}
|
||||
177
python/ql/src/experimental/semmle/python/libraries/SmtpLib.qll
Normal file
177
python/ql/src/experimental/semmle/python/libraries/SmtpLib.qll
Normal file
@@ -0,0 +1,177 @@
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import experimental.semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.dataflow.new.TaintTracking2
|
||||
|
||||
module SmtpLib {
|
||||
/** Gets a reference to `smtplib.SMTP_SSL` */
|
||||
private API::Node smtpConnectionInstance() {
|
||||
result = API::moduleImport("smtplib").getMember("SMTP_SSL")
|
||||
}
|
||||
|
||||
/** Gets a reference to `email.mime.multipart.MIMEMultipart` */
|
||||
private API::Node smtpMimeMultipartInstance() {
|
||||
result =
|
||||
API::moduleImport("email").getMember("mime").getMember("multipart").getMember("MIMEMultipart")
|
||||
}
|
||||
|
||||
/** Gets a reference to `email.mime.text.MIMEText` */
|
||||
private API::Node smtpMimeTextInstance() {
|
||||
result = API::moduleImport("email").getMember("mime").getMember("text").getMember("MIMEText")
|
||||
}
|
||||
|
||||
private DataFlow::CallCfgNode mimeText(string mimetype) {
|
||||
result = smtpMimeTextInstance().getACall() and
|
||||
[result.getArg(1), result.getArgByName("_subtype")].asExpr().(StrConst).getText() = mimetype
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets flow from `MIMEText()` to `MIMEMultipart(_subparts=(part1, part2))`'s `_subparts`
|
||||
* argument. Used because of the impossibility to get local source nodes from `_subparts`'
|
||||
* `(List|Tuple)` elements.
|
||||
*/
|
||||
private class SMTPMessageConfig extends TaintTracking2::Configuration {
|
||||
SMTPMessageConfig() { this = "SMTPMessageConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source = mimeText(_) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink = smtpMimeMultipartInstance().getACall().getArgByName("_subparts")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Using the `MimeText` call retrieves the content argument whose type argument equals `mimetype`.
|
||||
* This call flows into `MIMEMultipart`'s `_subparts` argument or the `.attach()` method call
|
||||
* and both local source nodes correlate to `smtp`'s `sendmail` call 3rd argument's local source.
|
||||
*
|
||||
* Given the following example with `getSmtpMessage(any(SmtpLibSendMail s), "html")`:
|
||||
*
|
||||
* ```py
|
||||
* part1 = MIMEText(text, "plain")
|
||||
* part2 = MIMEText(html, "html")
|
||||
* message = MIMEMultipart(_subparts=(part1, part2))
|
||||
* server.sendmail(sender_email, receiver_email, message.as_string())
|
||||
* ```
|
||||
*
|
||||
* * `source` would be `MIMEText(text, "html")`.
|
||||
* * `sink` would be `MIMEMultipart(_subparts=(part1, part2))`.
|
||||
* * Then `message` local source node is correlated to `sink`.
|
||||
* * Then the flow from `source` to `_subparts` is checked.
|
||||
*
|
||||
* Given the following example with `getSmtpMessage(any(SmtpLibSendMail s), "html")`:
|
||||
*
|
||||
* ```py
|
||||
* part1 = MIMEText(text, "plain")
|
||||
* part2 = MIMEText(html, "html")
|
||||
* message = MIMEMultipart("alternative")
|
||||
* message.attach(part1)
|
||||
* message.attach(part2)
|
||||
* server.sendmail(sender_email, receiver_email, message.as_string())
|
||||
* ```
|
||||
*
|
||||
* * `source` would be `MIMEText(text, "html")`.
|
||||
* * `sink` would be `message.attach(part2)`.
|
||||
* * Then `sink`'s object (`message`) local source is correlated to `server.sendmail`
|
||||
* 3rd argument local source (`MIMEMultipart("alternative")`).
|
||||
* * Then the flow from `source` to `sink` 1st argument is checked.
|
||||
*/
|
||||
bindingset[mimetype]
|
||||
private DataFlow::Node getSmtpMessage(DataFlow::CallCfgNode sendCall, string mimetype) {
|
||||
exists(DataFlow::Node source, DataFlow::Node sink |
|
||||
source = mimeText(mimetype) and
|
||||
(
|
||||
// via _subparts
|
||||
sink = smtpMimeMultipartInstance().getACall() and
|
||||
sink =
|
||||
[sendCall.getArg(2), sendCall.getArg(2).(DataFlow::MethodCallNode).getObject()]
|
||||
.getALocalSource() and
|
||||
any(SMTPMessageConfig a)
|
||||
.hasFlow(source, sink.(DataFlow::CallCfgNode).getArgByName("_subparts"))
|
||||
or
|
||||
// via .attach()
|
||||
sink = smtpMimeMultipartInstance().getReturn().getMember("attach").getACall() and
|
||||
sink.(DataFlow::MethodCallNode).getObject().getALocalSource() =
|
||||
[sendCall.getArg(2), sendCall.getArg(2).(DataFlow::MethodCallNode).getObject()]
|
||||
.getALocalSource() and
|
||||
source.(DataFlow::CallCfgNode).flowsTo(sink.(DataFlow::CallCfgNode).getArg(0))
|
||||
) and
|
||||
result = source.(DataFlow::CallCfgNode).getArg(0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a message subscript write by correlating subscript's object local source with
|
||||
* `smtp`'s `sendmail` call 3rd argument's local source.
|
||||
*
|
||||
* Given the following example with `getSMTPSubscriptByIndex(any(SmtpLibSendMail s), "Subject")`:
|
||||
*
|
||||
* ```py
|
||||
* message = MIMEMultipart("alternative")
|
||||
* message["Subject"] = "multipart test"
|
||||
* server.sendmail(sender_email, receiver_email, message.as_string())
|
||||
* ```
|
||||
*
|
||||
* * `def` would be `message["Subject"]` (`DefinitionNode`)
|
||||
* * `sub` would be `message["Subject"]` (`Subscript`)
|
||||
* * `result` would be `"multipart test"`
|
||||
*/
|
||||
private DataFlow::Node getSMTPSubscriptByIndex(DataFlow::CallCfgNode sendCall, string index) {
|
||||
exists(DefinitionNode def, Subscript sub |
|
||||
sub = def.getNode() and
|
||||
DataFlow::exprNode(sub.getObject()).getALocalSource() =
|
||||
[sendCall.getArg(2), sendCall.getArg(2).(DataFlow::MethodCallNode).getObject()]
|
||||
.getALocalSource() and
|
||||
sub.getIndex().(StrConst).getText() = index and
|
||||
result.asCfgNode() = def.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to `smtplib.SMTP_SSL().sendmail()`.
|
||||
*
|
||||
* Given the following example:
|
||||
*
|
||||
* ```py
|
||||
* part1 = MIMEText(text, "plain")
|
||||
* part2 = MIMEText(html, "html")
|
||||
*
|
||||
* message = MIMEMultipart(_subparts=(part1, part2))
|
||||
* message["Subject"] = "multipart test"
|
||||
* message["From"] = sender_email
|
||||
* message["To"] = receiver_email
|
||||
*
|
||||
* server.login(sender_email, "SERVER_PASSWORD")
|
||||
* server.sendmail(sender_email, receiver_email, message.as_string())
|
||||
* ```
|
||||
*
|
||||
* * `this` would be `server.sendmail(sender_email, receiver_email, message.as_string())`.
|
||||
* * `getPlainTextBody()`'s result would be `text`.
|
||||
* * `getHtmlBody()`'s result would be `html`.
|
||||
* * `getTo()`'s result would be `receiver_email`.
|
||||
* * `getFrom()`'s result would be `sender_email`.
|
||||
* * `getSubject()`'s result would be `"multipart test"`.
|
||||
*/
|
||||
private class SmtpLibSendMail extends DataFlow::CallCfgNode, EmailSender::Range {
|
||||
SmtpLibSendMail() {
|
||||
this = smtpConnectionInstance().getReturn().getMember("sendmail").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getPlainTextBody() { result = getSmtpMessage(this, "plain") }
|
||||
|
||||
override DataFlow::Node getHtmlBody() { result = getSmtpMessage(this, "html") }
|
||||
|
||||
override DataFlow::Node getTo() {
|
||||
result in [this.getArg(1), getSMTPSubscriptByIndex(this, "To")]
|
||||
}
|
||||
|
||||
override DataFlow::Node getFrom() {
|
||||
result in [this.getArg(0), getSMTPSubscriptByIndex(this, "From")]
|
||||
}
|
||||
|
||||
override DataFlow::Node getSubject() {
|
||||
result in [this.getArg(2), getSMTPSubscriptByIndex(this, "Subject")]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Provides a taint-tracking configuration for detecting reflected server-side
|
||||
* cross-site scripting vulnerabilities.
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
import semmle.python.dataflow.new.RemoteFlowSources
|
||||
import semmle.python.dataflow.new.BarrierGuards
|
||||
import experimental.semmle.python.Concepts
|
||||
import semmle.python.Concepts
|
||||
import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for detecting reflected server-side cross-site
|
||||
* scripting vulnerabilities.
|
||||
*/
|
||||
class ReflectedXssConfiguration extends TaintTracking::Configuration {
|
||||
ReflectedXssConfiguration() { this = "ReflectedXssConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink = any(EmailSender email).getHtmlBody() }
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof StringConstCompare
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node sanitizer) {
|
||||
sanitizer = any(HtmlEscaping esc).getOutput()
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
exists(DataFlow::CallCfgNode htmlContentCall |
|
||||
htmlContentCall =
|
||||
API::moduleImport("sendgrid")
|
||||
.getMember("helpers")
|
||||
.getMember("mail")
|
||||
.getMember("HtmlContent")
|
||||
.getACall() and
|
||||
nodeTo = htmlContentCall and
|
||||
nodeFrom = htmlContentCall.getArg(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -487,7 +487,8 @@ class CryptographicOperationTest extends InlineExpectationsTest {
|
||||
|
||||
override string getARelevantTag() {
|
||||
result in [
|
||||
"CryptographicOperation", "CryptographicOperationInput", "CryptographicOperationAlgorithm"
|
||||
"CryptographicOperation", "CryptographicOperationInput", "CryptographicOperationAlgorithm",
|
||||
"CryptographicOperationBlockMode"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -507,6 +508,10 @@ class CryptographicOperationTest extends InlineExpectationsTest {
|
||||
element = cryptoOperation.toString() and
|
||||
value = cryptoOperation.getAlgorithm().getName() and
|
||||
tag = "CryptographicOperationAlgorithm"
|
||||
or
|
||||
element = cryptoOperation.toString() and
|
||||
value = cryptoOperation.getBlockMode() and
|
||||
tag = "CryptographicOperationBlockMode"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
edges
|
||||
| flask_mail.py:13:22:13:28 | ControlFlowNode for request | flask_mail.py:13:22:13:33 | ControlFlowNode for Attribute |
|
||||
| flask_mail.py:13:22:13:28 | ControlFlowNode for request | flask_mail.py:18:14:18:25 | ControlFlowNode for Attribute |
|
||||
| flask_mail.py:13:22:13:33 | ControlFlowNode for Attribute | flask_mail.py:13:22:13:41 | ControlFlowNode for Subscript |
|
||||
| flask_mail.py:18:14:18:20 | ControlFlowNode for request | flask_mail.py:18:14:18:25 | ControlFlowNode for Attribute |
|
||||
| flask_mail.py:18:14:18:25 | ControlFlowNode for Attribute | flask_mail.py:18:14:18:33 | ControlFlowNode for Subscript |
|
||||
| flask_mail.py:31:24:31:30 | ControlFlowNode for request | flask_mail.py:31:24:31:35 | ControlFlowNode for Attribute |
|
||||
| flask_mail.py:31:24:31:35 | ControlFlowNode for Attribute | flask_mail.py:31:24:31:43 | ControlFlowNode for Subscript |
|
||||
| sendgrid_mail.py:14:22:14:28 | ControlFlowNode for request | sendgrid_mail.py:14:22:14:33 | ControlFlowNode for Attribute |
|
||||
| sendgrid_mail.py:14:22:14:33 | ControlFlowNode for Attribute | sendgrid_mail.py:14:22:14:49 | ControlFlowNode for Subscript |
|
||||
| sendgrid_mail.py:26:34:26:40 | ControlFlowNode for request | sendgrid_mail.py:26:34:26:45 | ControlFlowNode for Attribute |
|
||||
| sendgrid_mail.py:26:34:26:45 | ControlFlowNode for Attribute | sendgrid_mail.py:26:34:26:61 | ControlFlowNode for Subscript |
|
||||
| sendgrid_mail.py:26:34:26:61 | ControlFlowNode for Subscript | sendgrid_mail.py:26:22:26:62 | ControlFlowNode for HtmlContent() |
|
||||
| sendgrid_mail.py:37:41:37:47 | ControlFlowNode for request | sendgrid_mail.py:37:41:37:52 | ControlFlowNode for Attribute |
|
||||
| sendgrid_mail.py:37:41:37:52 | ControlFlowNode for Attribute | sendgrid_mail.py:37:41:37:68 | ControlFlowNode for Subscript |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:57 | ControlFlowNode for request | sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:62 | ControlFlowNode for Attribute |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:57 | ControlFlowNode for request | sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:61 | ControlFlowNode for Attribute |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:57 | ControlFlowNode for request | sendgrid_via_mail_send_post_request_body_bad.py:41:50:41:61 | ControlFlowNode for Attribute |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:62 | ControlFlowNode for Attribute | sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:78 | ControlFlowNode for Subscript |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:78 | ControlFlowNode for Subscript | sendgrid_via_mail_send_post_request_body_bad.py:16:26:16:79 | ControlFlowNode for Attribute() |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:56 | ControlFlowNode for request | sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:61 | ControlFlowNode for Attribute |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:56 | ControlFlowNode for request | sendgrid_via_mail_send_post_request_body_bad.py:41:50:41:61 | ControlFlowNode for Attribute |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:61 | ControlFlowNode for Attribute | sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:76 | ControlFlowNode for Subscript |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:76 | ControlFlowNode for Subscript | sendgrid_via_mail_send_post_request_body_bad.py:27:25:27:77 | ControlFlowNode for Attribute() |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:41:50:41:56 | ControlFlowNode for request | sendgrid_via_mail_send_post_request_body_bad.py:41:50:41:61 | ControlFlowNode for Attribute |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:41:50:41:61 | ControlFlowNode for Attribute | sendgrid_via_mail_send_post_request_body_bad.py:41:50:41:78 | ControlFlowNode for Subscript |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:41:50:41:78 | ControlFlowNode for Subscript | sendgrid_via_mail_send_post_request_body_bad.py:41:25:41:79 | ControlFlowNode for Attribute() |
|
||||
| smtplib_bad_subparts.py:17:12:17:18 | ControlFlowNode for request | smtplib_bad_subparts.py:17:12:17:23 | ControlFlowNode for Attribute |
|
||||
| smtplib_bad_subparts.py:17:12:17:23 | ControlFlowNode for Attribute | smtplib_bad_subparts.py:17:12:17:33 | ControlFlowNode for Subscript |
|
||||
| smtplib_bad_subparts.py:17:12:17:33 | ControlFlowNode for Subscript | smtplib_bad_subparts.py:24:22:24:25 | ControlFlowNode for html |
|
||||
| smtplib_bad_via_attach.py:20:12:20:18 | ControlFlowNode for request | smtplib_bad_via_attach.py:20:12:20:23 | ControlFlowNode for Attribute |
|
||||
| smtplib_bad_via_attach.py:20:12:20:23 | ControlFlowNode for Attribute | smtplib_bad_via_attach.py:20:12:20:31 | ControlFlowNode for Subscript |
|
||||
| smtplib_bad_via_attach.py:20:12:20:31 | ControlFlowNode for Subscript | smtplib_bad_via_attach.py:27:22:27:25 | ControlFlowNode for html |
|
||||
nodes
|
||||
| django_mail.py:14:48:14:82 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| django_mail.py:23:30:23:64 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| django_mail.py:25:32:25:66 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| flask_mail.py:13:22:13:28 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| flask_mail.py:13:22:13:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| flask_mail.py:13:22:13:41 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| flask_mail.py:18:14:18:20 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| flask_mail.py:18:14:18:25 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| flask_mail.py:18:14:18:33 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| flask_mail.py:31:24:31:30 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| flask_mail.py:31:24:31:35 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| flask_mail.py:31:24:31:43 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| sendgrid_mail.py:14:22:14:28 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| sendgrid_mail.py:14:22:14:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| sendgrid_mail.py:14:22:14:49 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| sendgrid_mail.py:26:22:26:62 | ControlFlowNode for HtmlContent() | semmle.label | ControlFlowNode for HtmlContent() |
|
||||
| sendgrid_mail.py:26:34:26:40 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| sendgrid_mail.py:26:34:26:45 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| sendgrid_mail.py:26:34:26:61 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| sendgrid_mail.py:37:41:37:47 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| sendgrid_mail.py:37:41:37:52 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| sendgrid_mail.py:37:41:37:68 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:16:26:16:79 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:57 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:62 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:78 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:27:25:27:77 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:56 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:61 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:76 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:41:25:41:79 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:41:50:41:56 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:41:50:41:61 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:41:50:41:78 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| smtplib_bad_subparts.py:17:12:17:18 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| smtplib_bad_subparts.py:17:12:17:23 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| smtplib_bad_subparts.py:17:12:17:33 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| smtplib_bad_subparts.py:24:22:24:25 | ControlFlowNode for html | semmle.label | ControlFlowNode for html |
|
||||
| smtplib_bad_via_attach.py:20:12:20:18 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| smtplib_bad_via_attach.py:20:12:20:23 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| smtplib_bad_via_attach.py:20:12:20:31 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| smtplib_bad_via_attach.py:27:22:27:25 | ControlFlowNode for html | semmle.label | ControlFlowNode for html |
|
||||
subpaths
|
||||
#select
|
||||
| django_mail.py:14:48:14:82 | ControlFlowNode for Attribute() | django_mail.py:14:48:14:82 | ControlFlowNode for Attribute() | django_mail.py:14:48:14:82 | ControlFlowNode for Attribute() | Cross-site scripting vulnerability due to $@. | django_mail.py:14:48:14:82 | ControlFlowNode for Attribute() | a user-provided value |
|
||||
| django_mail.py:23:30:23:64 | ControlFlowNode for Attribute() | django_mail.py:23:30:23:64 | ControlFlowNode for Attribute() | django_mail.py:23:30:23:64 | ControlFlowNode for Attribute() | Cross-site scripting vulnerability due to $@. | django_mail.py:23:30:23:64 | ControlFlowNode for Attribute() | a user-provided value |
|
||||
| django_mail.py:25:32:25:66 | ControlFlowNode for Attribute() | django_mail.py:25:32:25:66 | ControlFlowNode for Attribute() | django_mail.py:25:32:25:66 | ControlFlowNode for Attribute() | Cross-site scripting vulnerability due to $@. | django_mail.py:25:32:25:66 | ControlFlowNode for Attribute() | a user-provided value |
|
||||
| flask_mail.py:13:22:13:41 | ControlFlowNode for Subscript | flask_mail.py:13:22:13:28 | ControlFlowNode for request | flask_mail.py:13:22:13:41 | ControlFlowNode for Subscript | Cross-site scripting vulnerability due to $@. | flask_mail.py:13:22:13:28 | ControlFlowNode for request | a user-provided value |
|
||||
| flask_mail.py:18:14:18:33 | ControlFlowNode for Subscript | flask_mail.py:13:22:13:28 | ControlFlowNode for request | flask_mail.py:18:14:18:33 | ControlFlowNode for Subscript | Cross-site scripting vulnerability due to $@. | flask_mail.py:13:22:13:28 | ControlFlowNode for request | a user-provided value |
|
||||
| flask_mail.py:18:14:18:33 | ControlFlowNode for Subscript | flask_mail.py:18:14:18:20 | ControlFlowNode for request | flask_mail.py:18:14:18:33 | ControlFlowNode for Subscript | Cross-site scripting vulnerability due to $@. | flask_mail.py:18:14:18:20 | ControlFlowNode for request | a user-provided value |
|
||||
| flask_mail.py:31:24:31:43 | ControlFlowNode for Subscript | flask_mail.py:31:24:31:30 | ControlFlowNode for request | flask_mail.py:31:24:31:43 | ControlFlowNode for Subscript | Cross-site scripting vulnerability due to $@. | flask_mail.py:31:24:31:30 | ControlFlowNode for request | a user-provided value |
|
||||
| sendgrid_mail.py:14:22:14:49 | ControlFlowNode for Subscript | sendgrid_mail.py:14:22:14:28 | ControlFlowNode for request | sendgrid_mail.py:14:22:14:49 | ControlFlowNode for Subscript | Cross-site scripting vulnerability due to $@. | sendgrid_mail.py:14:22:14:28 | ControlFlowNode for request | a user-provided value |
|
||||
| sendgrid_mail.py:26:22:26:62 | ControlFlowNode for HtmlContent() | sendgrid_mail.py:26:34:26:40 | ControlFlowNode for request | sendgrid_mail.py:26:22:26:62 | ControlFlowNode for HtmlContent() | Cross-site scripting vulnerability due to $@. | sendgrid_mail.py:26:34:26:40 | ControlFlowNode for request | a user-provided value |
|
||||
| sendgrid_mail.py:37:41:37:68 | ControlFlowNode for Subscript | sendgrid_mail.py:37:41:37:47 | ControlFlowNode for request | sendgrid_mail.py:37:41:37:68 | ControlFlowNode for Subscript | Cross-site scripting vulnerability due to $@. | sendgrid_mail.py:37:41:37:47 | ControlFlowNode for request | a user-provided value |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:16:26:16:79 | ControlFlowNode for Attribute() | sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:57 | ControlFlowNode for request | sendgrid_via_mail_send_post_request_body_bad.py:16:26:16:79 | ControlFlowNode for Attribute() | Cross-site scripting vulnerability due to $@. | sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:57 | ControlFlowNode for request | a user-provided value |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:27:25:27:77 | ControlFlowNode for Attribute() | sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:57 | ControlFlowNode for request | sendgrid_via_mail_send_post_request_body_bad.py:27:25:27:77 | ControlFlowNode for Attribute() | Cross-site scripting vulnerability due to $@. | sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:57 | ControlFlowNode for request | a user-provided value |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:27:25:27:77 | ControlFlowNode for Attribute() | sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:56 | ControlFlowNode for request | sendgrid_via_mail_send_post_request_body_bad.py:27:25:27:77 | ControlFlowNode for Attribute() | Cross-site scripting vulnerability due to $@. | sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:56 | ControlFlowNode for request | a user-provided value |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:41:25:41:79 | ControlFlowNode for Attribute() | sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:57 | ControlFlowNode for request | sendgrid_via_mail_send_post_request_body_bad.py:41:25:41:79 | ControlFlowNode for Attribute() | Cross-site scripting vulnerability due to $@. | sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:57 | ControlFlowNode for request | a user-provided value |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:41:25:41:79 | ControlFlowNode for Attribute() | sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:56 | ControlFlowNode for request | sendgrid_via_mail_send_post_request_body_bad.py:41:25:41:79 | ControlFlowNode for Attribute() | Cross-site scripting vulnerability due to $@. | sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:56 | ControlFlowNode for request | a user-provided value |
|
||||
| sendgrid_via_mail_send_post_request_body_bad.py:41:25:41:79 | ControlFlowNode for Attribute() | sendgrid_via_mail_send_post_request_body_bad.py:41:50:41:56 | ControlFlowNode for request | sendgrid_via_mail_send_post_request_body_bad.py:41:25:41:79 | ControlFlowNode for Attribute() | Cross-site scripting vulnerability due to $@. | sendgrid_via_mail_send_post_request_body_bad.py:41:50:41:56 | ControlFlowNode for request | a user-provided value |
|
||||
| smtplib_bad_subparts.py:24:22:24:25 | ControlFlowNode for html | smtplib_bad_subparts.py:17:12:17:18 | ControlFlowNode for request | smtplib_bad_subparts.py:24:22:24:25 | ControlFlowNode for html | Cross-site scripting vulnerability due to $@. | smtplib_bad_subparts.py:17:12:17:18 | ControlFlowNode for request | a user-provided value |
|
||||
| smtplib_bad_via_attach.py:27:22:27:25 | ControlFlowNode for html | smtplib_bad_via_attach.py:20:12:20:18 | ControlFlowNode for request | smtplib_bad_via_attach.py:27:22:27:25 | ControlFlowNode for html | Cross-site scripting vulnerability due to $@. | smtplib_bad_via_attach.py:20:12:20:18 | ControlFlowNode for request | a user-provided value |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE-079/ReflectedXSS.ql
|
||||
@@ -0,0 +1,25 @@
|
||||
import django.http
|
||||
from django.core.mail import send_mail, mail_admins, mail_managers
|
||||
|
||||
|
||||
def django_response(request):
|
||||
"""
|
||||
The Django.core.mail#send_mail function source code can be found in the link below:
|
||||
https://github.com/django/django/blob/ca9872905559026af82000e46cde6f7dedc897b6/django/core/mail/__init__.py#L38
|
||||
|
||||
send_mass_mail does not provide html_message as an argument to it's function. See the link below for more info:
|
||||
https://github.com/django/django/blob/ca9872905559026af82000e46cde6f7dedc897b6/django/core/mail/__init__.py#L64
|
||||
"""
|
||||
send_mail("Subject", "plain-text body", "from@example.com",
|
||||
["to@example.com"], html_message=django.http.request.GET.get("html"))
|
||||
|
||||
|
||||
def django_response(request):
|
||||
"""
|
||||
The Django.core.mail#mail_admins and Django.core.mail#mail_managers functions source code can be found in the link below:
|
||||
https://github.com/django/django/blob/ca9872905559026af82000e46cde6f7dedc897b6/django/core/mail/__init__.py#L90-L121
|
||||
"""
|
||||
mail_admins("Subject", "plain-text body",
|
||||
html_message=django.http.request.GET.get("html"))
|
||||
mail_managers("Subject", "plain-text body",
|
||||
html_message=django.http.request.GET.get("html"))
|
||||
@@ -0,0 +1,32 @@
|
||||
from flask import request, Flask
|
||||
from flask_mail import Mail, Message
|
||||
|
||||
app = Flask(__name__)
|
||||
mail = Mail(app)
|
||||
|
||||
@app.route("/send")
|
||||
def send():
|
||||
msg = Message(subject="Subject",
|
||||
sender="from@example.com",
|
||||
recipients=["to@example.com"],
|
||||
body="plain-text body",
|
||||
html=request.args["html"])
|
||||
|
||||
# The message can contain a body and/or HTML:
|
||||
msg.body = "plain-text body"
|
||||
# The email's HTML can be set via msg.html or as an initialize argument when creating a Message object.
|
||||
msg.html = request.args["html"]
|
||||
|
||||
mail.send(msg)
|
||||
|
||||
@app.route("/connect")
|
||||
def connect():
|
||||
"""
|
||||
Minimal example to test mail.connect() usage
|
||||
"""
|
||||
with mail.connect() as conn:
|
||||
msg = Message(subject="Subject",
|
||||
sender="from@example.com",
|
||||
recipients=["to@example.com"],
|
||||
html=request.args["html"])
|
||||
conn.send(msg)
|
||||
@@ -0,0 +1,57 @@
|
||||
from flask import request, Flask
|
||||
from sendgrid import SendGridAPIClient
|
||||
from sendgrid.helpers.mail import Mail, Email, To, Content, MimeType, HtmlContent
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route("/send")
|
||||
def send():
|
||||
message = Mail(
|
||||
from_email='from_email@example.com',
|
||||
to_emails='to@example.com',
|
||||
subject='Sending with Twilio SendGrid is Fun',
|
||||
html_content=request.args["html_content"])
|
||||
|
||||
sg = SendGridAPIClient('SENDGRID_API_KEY')
|
||||
sg.send(message)
|
||||
|
||||
|
||||
@app.route("/send-HtmlContent")
|
||||
def send():
|
||||
message = Mail(
|
||||
from_email='from_email@example.com',
|
||||
to_emails='to@example.com',
|
||||
subject='Sending with Twilio SendGrid is Fun',
|
||||
html_content=HtmlContent(request.args["html_content"]))
|
||||
|
||||
sg = SendGridAPIClient('SENDGRID_API_KEY')
|
||||
sg.send(message)
|
||||
|
||||
|
||||
@app.route("/send_post")
|
||||
def send_post():
|
||||
from_email = Email("test@example.com")
|
||||
to_email = To("test@example.com")
|
||||
subject = "Sending with SendGrid is Fun"
|
||||
html_content = Content("text/html", request.args["html_content"])
|
||||
plain_content = Content("text/plain", request.args["plain_content"])
|
||||
|
||||
mail = Mail(from_email, to_email, subject, plain_content, html_content)
|
||||
|
||||
sg = SendGridAPIClient(api_key='SENDGRID_API_KEY')
|
||||
response = sg.client.mail.send.post(request_body=mail.get())
|
||||
|
||||
|
||||
@app.route("/send_post2")
|
||||
def send_post2():
|
||||
from_email = Email("test@example.com")
|
||||
to_email = To("test@example.com")
|
||||
subject = "Sending with SendGrid is Fun"
|
||||
html_content = Content(MimeType.html, request.args["html_content"])
|
||||
plain_content = Content(MimeType.text, request.args["plain_content"])
|
||||
|
||||
mail = Mail(from_email, to_email, subject, plain_content, html_content)
|
||||
|
||||
sg = SendGridAPIClient(api_key='SENDGRID_API_KEY')
|
||||
response = sg.client.mail.send.post(request_body=mail.get())
|
||||
@@ -0,0 +1,48 @@
|
||||
import sendgrid
|
||||
import os
|
||||
from flask import request, Flask
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route("/sendgrid")
|
||||
def send():
|
||||
sg = sendgrid.SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
|
||||
|
||||
data = {
|
||||
"content": [
|
||||
{
|
||||
"type": "text/html",
|
||||
"value": "<html>{}</html>".format(request.args["html_content"])
|
||||
}
|
||||
],
|
||||
"from": {
|
||||
"email": "sam.smith@example.com",
|
||||
"name": "Sam Smith"
|
||||
},
|
||||
"headers": {},
|
||||
"mail_settings": {
|
||||
"footer": {
|
||||
"enable": True,
|
||||
"html": "<html>{}</html>".format(request.args["html_footer"]),
|
||||
"text": "Thanks,/n The SendGrid Team"
|
||||
},
|
||||
},
|
||||
"reply_to": {
|
||||
"email": "sam.smith@example.com",
|
||||
"name": "Sam Smith"
|
||||
},
|
||||
"send_at": 1409348513,
|
||||
"subject": "Hello, World!",
|
||||
"template_id": "[YOUR TEMPLATE ID GOES HERE]",
|
||||
"tracking_settings": {
|
||||
"subscription_tracking": {
|
||||
"enable": True,
|
||||
"html": "<html>{}</html>".format(request.args["html_tracking"]),
|
||||
"substitution_tag": "<%click here%>",
|
||||
"text": "If you would like to unsubscribe and stop receiving these emails <% click here %>."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response = sg.client.mail.send.post(request_body=data)
|
||||
@@ -0,0 +1,42 @@
|
||||
# This test checks that the developer doesn't pass a MIMEText instance to a MIMEMultipart initializer via the subparts parameter.
|
||||
from flask import Flask, request
|
||||
import json
|
||||
import smtplib
|
||||
import ssl
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def email_person():
|
||||
sender_email = "sender@gmail.com"
|
||||
receiver_email = "receiver@example.com"
|
||||
|
||||
name = request.args['search']
|
||||
# Create the plain-text and HTML version of your message
|
||||
text = "hello there"
|
||||
html = f"hello {name}"
|
||||
|
||||
# Turn these into plain/html MIMEText objects
|
||||
part1 = MIMEText(text, "plain")
|
||||
part2 = MIMEText(html, "html")
|
||||
|
||||
message = MIMEMultipart(_subparts=(part1, part2))
|
||||
message["Subject"] = "multipart test"
|
||||
message["From"] = sender_email
|
||||
message["To"] = receiver_email
|
||||
|
||||
# Create secure connection with server and send email
|
||||
context = ssl.create_default_context()
|
||||
server = smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context)
|
||||
|
||||
server.login(sender_email, "SERVER_PASSWORD")
|
||||
server.sendmail(
|
||||
sender_email, receiver_email, message.as_string()
|
||||
)
|
||||
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# app.run(debug=True)
|
||||
@@ -0,0 +1,45 @@
|
||||
# This test checks that the developer doesn't pass a MIMEText instance to a MIMEMultipart message.
|
||||
from flask import Flask, request
|
||||
import json
|
||||
import smtplib, ssl
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route("/")
|
||||
def email_person():
|
||||
sender_email = "sender@gmail.com"
|
||||
receiver_email = "receiver@example.com"
|
||||
|
||||
message = MIMEMultipart("alternative")
|
||||
message["Subject"] = "multipart test"
|
||||
message["From"] = sender_email
|
||||
message["To"] = receiver_email
|
||||
|
||||
name = request.args['name']
|
||||
# Create the plain-text and HTML version of your message
|
||||
text = "hello there"
|
||||
html = f"hello {name}"
|
||||
|
||||
# Turn these into plain/html MIMEText objects
|
||||
part1 = MIMEText(text, "plain")
|
||||
part2 = MIMEText(html, "html")
|
||||
|
||||
# Add HTML/plain-text parts to MIMEMultipart message
|
||||
# The email client will try to render the last part first
|
||||
message.attach(part1)
|
||||
message.attach(part2)
|
||||
|
||||
# Create secure connection with server and send email
|
||||
context = ssl.create_default_context()
|
||||
server = smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context)
|
||||
|
||||
server.login(sender_email, "SERVER_PASSWORD")
|
||||
server.sendmail(
|
||||
sender_email, receiver_email, message.as_string()
|
||||
)
|
||||
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# app.run(debug=True)
|
||||
@@ -1,5 +1,6 @@
|
||||
import aiohttp
|
||||
import asyncio
|
||||
import ssl
|
||||
|
||||
s = aiohttp.ClientSession()
|
||||
resp = s.request("method", "url") # $ clientRequestUrlPart="url"
|
||||
@@ -13,4 +14,22 @@ with aiohttp.ClientSession() as session:
|
||||
s = aiohttp.ClientSession()
|
||||
resp = s.post("url") # $ clientRequestUrlPart="url"
|
||||
resp = s.patch("url") # $ clientRequestUrlPart="url"
|
||||
resp = s.options("url") # $ clientRequestUrlPart="url"
|
||||
resp = s.options("url") # $ clientRequestUrlPart="url"
|
||||
|
||||
# disabling of SSL validation
|
||||
# see https://docs.aiohttp.org/en/stable/client_reference.html#aiohttp.ClientSession.request
|
||||
s.get("url", ssl=False) # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
|
||||
s.get("url", ssl=0) # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
|
||||
# None is treated as default and so does _not_ disable the check
|
||||
s.get("url", ssl=None) # $ clientRequestUrlPart="url"
|
||||
|
||||
# deprecated since 3.0, but still supported
|
||||
s.get("url", verify_ssl=False) # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
|
||||
|
||||
# A manually constructed SSLContext does not have safe defaults, so is effectively the
|
||||
# same as turning off SSL validation
|
||||
context = ssl.SSLContext()
|
||||
assert context.check_hostname == False
|
||||
assert context.verify_mode == ssl.VerifyMode.CERT_NONE
|
||||
|
||||
s.get("url", ssl=context) # $ clientRequestUrlPart="url" MISSING: clientRequestCertValidationDisabled
|
||||
|
||||
@@ -21,14 +21,14 @@ padding = b"\0"*padding_len
|
||||
cipher = AES.new(key, AES.MODE_CBC, iv=iv)
|
||||
# using separate .encrypt calls on individual lines does not work
|
||||
whole_plantext = secret_message + padding
|
||||
encrypted = cipher.encrypt(whole_plantext) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=whole_plantext
|
||||
encrypted = cipher.encrypt(whole_plantext) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=whole_plantext CryptographicOperationBlockMode=CBC
|
||||
|
||||
print("encrypted={}".format(encrypted))
|
||||
|
||||
print()
|
||||
|
||||
cipher = AES.new(key, AES.MODE_CBC, iv=iv)
|
||||
decrypted = cipher.decrypt(encrypted) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=encrypted
|
||||
decrypted = cipher.decrypt(encrypted) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=encrypted CryptographicOperationBlockMode=CBC
|
||||
|
||||
decrypted = decrypted[:-padding_len]
|
||||
|
||||
|
||||
@@ -21,14 +21,14 @@ padding = b"\0"*padding_len
|
||||
cipher = AES.new(key, AES.MODE_CBC, iv=iv)
|
||||
# using separate .encrypt calls on individual lines does not work
|
||||
whole_plantext = secret_message + padding
|
||||
encrypted = cipher.encrypt(whole_plantext) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=whole_plantext
|
||||
encrypted = cipher.encrypt(whole_plantext) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=whole_plantext CryptographicOperationBlockMode=CBC
|
||||
|
||||
print("encrypted={}".format(encrypted))
|
||||
|
||||
print()
|
||||
|
||||
cipher = AES.new(key, AES.MODE_CBC, iv=iv)
|
||||
decrypted = cipher.decrypt(encrypted) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=encrypted
|
||||
decrypted = cipher.decrypt(encrypted) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=encrypted CryptographicOperationBlockMode=CBC
|
||||
|
||||
decrypted = decrypted[:-padding_len]
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ padding = b"\0"*padding_len
|
||||
|
||||
encryptor = cipher.encryptor()
|
||||
print(padding_len)
|
||||
encrypted = encryptor.update(secret_message) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=secret_message
|
||||
encrypted += encryptor.update(padding) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=padding
|
||||
encrypted = encryptor.update(secret_message) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=secret_message CryptographicOperationBlockMode=CBC
|
||||
encrypted += encryptor.update(padding) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=padding CryptographicOperationBlockMode=CBC
|
||||
encrypted += encryptor.finalize()
|
||||
|
||||
print("encrypted={}".format(encrypted))
|
||||
@@ -31,7 +31,7 @@ print("encrypted={}".format(encrypted))
|
||||
print()
|
||||
|
||||
decryptor = cipher.decryptor()
|
||||
decrypted = decryptor.update(encrypted) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=encrypted
|
||||
decrypted = decryptor.update(encrypted) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=encrypted CryptographicOperationBlockMode=CBC
|
||||
decrypted += decryptor.finalize()
|
||||
|
||||
decrypted = decrypted[:-padding_len]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import httpx
|
||||
import ssl
|
||||
|
||||
httpx.get("url") # $ clientRequestUrlPart="url"
|
||||
httpx.post("url") # $ clientRequestUrlPart="url"
|
||||
@@ -15,10 +16,30 @@ response = client.options("url") # $ clientRequestUrlPart="url"
|
||||
response = client.request("method", url="url") # $ clientRequestUrlPart="url"
|
||||
response = client.stream("method", url="url") # $ clientRequestUrlPart="url"
|
||||
|
||||
client = httpx.AsyncClient()
|
||||
response = client.get("url") # $ clientRequestUrlPart="url"
|
||||
response = client.post("url") # $ clientRequestUrlPart="url"
|
||||
response = client.patch("url") # $ clientRequestUrlPart="url"
|
||||
response = client.options("url") # $ clientRequestUrlPart="url"
|
||||
response = client.request("method", url="url") # $ clientRequestUrlPart="url"
|
||||
response = client.stream("method", url="url") # $ clientRequestUrlPart="url"
|
||||
async def async_test():
|
||||
client = httpx.AsyncClient()
|
||||
response = await client.get("url") # $ clientRequestUrlPart="url"
|
||||
response = await client.post("url") # $ clientRequestUrlPart="url"
|
||||
response = await client.patch("url") # $ clientRequestUrlPart="url"
|
||||
response = await client.options("url") # $ clientRequestUrlPart="url"
|
||||
response = await client.request("method", url="url") # $ clientRequestUrlPart="url"
|
||||
response = await client.stream("method", url="url") # $ clientRequestUrlPart="url"
|
||||
|
||||
# ==============================================================================
|
||||
# Disabling certificate validation
|
||||
# ==============================================================================
|
||||
|
||||
httpx.get("url", verify=False) # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
|
||||
httpx.get("url", verify=0) # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
|
||||
httpx.get("url", verify=None) # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
|
||||
|
||||
# A manually constructed SSLContext does not have safe defaults, so is effectively the
|
||||
# same as turning off SSL validation
|
||||
context = ssl.SSLContext()
|
||||
assert context.check_hostname == False
|
||||
assert context.verify_mode == ssl.VerifyMode.CERT_NONE
|
||||
|
||||
httpx.get("url", verify=context) # $ clientRequestUrlPart="url" MISSING: clientRequestCertValidationDisabled
|
||||
|
||||
client = httpx.Client(verify=False)
|
||||
client.get("url") # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
|
||||
|
||||
@@ -33,6 +33,10 @@ resp = requests.options("url") # $ clientRequestUrlPart="url"
|
||||
# ==============================================================================
|
||||
|
||||
resp = requests.get("url", verify=False) # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
|
||||
resp = requests.get("url", verify=0) # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
|
||||
|
||||
# in reuqests, using `verify=None` is just the default value, so does NOT turn off certificate validation
|
||||
resp = requests.get("url", verify=None) # $ clientRequestUrlPart="url"
|
||||
|
||||
def make_get(verify_arg):
|
||||
resp = requests.get("url", verify=verify_arg) # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
|
||||
|
||||
@@ -1,7 +1,20 @@
|
||||
import urllib2
|
||||
import ssl
|
||||
|
||||
resp = urllib2.Request("url") # $ clientRequestUrlPart="url"
|
||||
resp = urllib2.Request(url="url") # $ clientRequestUrlPart="url"
|
||||
|
||||
resp = urllib2.urlopen("url") # $ clientRequestUrlPart="url"
|
||||
resp = urllib2.urlopen(url="url") # $ clientRequestUrlPart="url"
|
||||
resp = urllib2.urlopen(url="url") # $ clientRequestUrlPart="url"
|
||||
|
||||
# ==============================================================================
|
||||
# Certificate validation disabled
|
||||
# ==============================================================================
|
||||
|
||||
# A manually constructed SSLContext does not have safe defaults, so is effectively the
|
||||
# same as turning off SSL validation
|
||||
context = ssl.SSLContext()
|
||||
assert context.check_hostname == False
|
||||
assert context.verify_mode == ssl.VerifyMode.CERT_NONE
|
||||
|
||||
urllib2.urlopen("url", context=context) # $ clientRequestUrlPart="url" MISSING: clientRequestCertValidationDisabled
|
||||
|
||||
@@ -1,7 +1,20 @@
|
||||
import ssl
|
||||
from urllib.request import Request, urlopen
|
||||
|
||||
Request("url") # $ clientRequestUrlPart="url"
|
||||
Request(url="url") # $ clientRequestUrlPart="url"
|
||||
|
||||
urlopen("url") # $ clientRequestUrlPart="url"
|
||||
urlopen(url="url") # $ clientRequestUrlPart="url"
|
||||
urlopen(url="url") # $ clientRequestUrlPart="url"
|
||||
|
||||
# ==============================================================================
|
||||
# Certificate validation disabled
|
||||
# ==============================================================================
|
||||
|
||||
# A manually constructed SSLContext does not have safe defaults, so is effectively the
|
||||
# same as turning off SSL validation
|
||||
context = ssl.SSLContext()
|
||||
assert context.check_hostname == False
|
||||
assert context.verify_mode == ssl.VerifyMode.CERT_NONE
|
||||
|
||||
urlopen("url", context=context) # $ clientRequestUrlPart="url" MISSING: clientRequestCertValidationDisabled
|
||||
|
||||
@@ -27,3 +27,33 @@ resp = pool.request("method", "url") # $ clientRequestUrlPart="url"
|
||||
resp = pool.request("method", url="url") # $ clientRequestUrlPart="url"
|
||||
resp = pool.urlopen("method", "url") # $ clientRequestUrlPart="url"
|
||||
resp = pool.urlopen("method", url="url") # $ clientRequestUrlPart="url"
|
||||
|
||||
# ==============================================================================
|
||||
# Certificate validation disabled
|
||||
# ==============================================================================
|
||||
|
||||
# see https://docs.python.org/3.10/library/ssl.html#ssl.CERT_NONE
|
||||
pool = urllib3.HTTPSConnectionPool("host", cert_reqs='CERT_NONE')
|
||||
resp = pool.request("method", "url") # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
|
||||
|
||||
pool = urllib3.HTTPSConnectionPool("host", assert_hostname=False)
|
||||
resp = pool.request("method", "url") # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
|
||||
|
||||
pool = urllib3.HTTPSConnectionPool("host", cert_reqs='CERT_NONE', assert_hostname=False)
|
||||
resp = pool.request("method", "url") # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
|
||||
|
||||
|
||||
# same with PoolManager
|
||||
pool = urllib3.PoolManager(cert_reqs='CERT_NONE')
|
||||
resp = pool.request("method", "url") # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
|
||||
|
||||
pool = urllib3.PoolManager(assert_hostname=False)
|
||||
resp = pool.request("method", "url") # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
|
||||
|
||||
|
||||
# and same with ProxyManager
|
||||
pool = urllib3.ProxyManager("https://proxy", cert_reqs='CERT_NONE')
|
||||
resp = pool.request("method", "url") # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
|
||||
|
||||
pool = urllib3.ProxyManager("https://proxy", assert_hostname=False)
|
||||
resp = pool.request("method", "url") # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
| make_request.py:5:1:5:48 | ControlFlowNode for Attribute() | Call to requests.get with verify=$@ | make_request.py:5:43:5:47 | ControlFlowNode for False | False |
|
||||
| make_request.py:7:1:7:49 | ControlFlowNode for Attribute() | Call to requests.post with verify=$@ | make_request.py:7:44:7:48 | ControlFlowNode for False | False |
|
||||
| make_request.py:12:1:12:39 | ControlFlowNode for put() | Call to requests.put with verify=$@ | make_request.py:12:34:12:38 | ControlFlowNode for False | False |
|
||||
| make_request.py:28:5:28:46 | ControlFlowNode for patch() | Call to requests.patch with verify=$@ | make_request.py:30:6:30:10 | ControlFlowNode for False | False |
|
||||
| make_request.py:34:1:34:45 | ControlFlowNode for Attribute() | Call to requests.post with verify=$@ | make_request.py:34:44:34:44 | ControlFlowNode for IntegerLiteral | False |
|
||||
| make_request.py:5:1:5:48 | ControlFlowNode for Attribute() | This request may run without certificate validation because it is $@. | make_request.py:5:43:5:47 | ControlFlowNode for False | disabled here | make_request.py:5:43:5:47 | ControlFlowNode for False | here |
|
||||
| make_request.py:7:1:7:49 | ControlFlowNode for Attribute() | This request may run without certificate validation because it is $@. | make_request.py:7:44:7:48 | ControlFlowNode for False | disabled here | make_request.py:7:44:7:48 | ControlFlowNode for False | here |
|
||||
| make_request.py:12:1:12:39 | ControlFlowNode for put() | This request may run without certificate validation because it is $@. | make_request.py:12:34:12:38 | ControlFlowNode for False | disabled here | make_request.py:12:34:12:38 | ControlFlowNode for False | here |
|
||||
| make_request.py:28:5:28:46 | ControlFlowNode for patch() | This request may run without certificate validation because it is $@ by the value from $@. | make_request.py:28:40:28:45 | ControlFlowNode for verify | disabled here | make_request.py:30:6:30:10 | ControlFlowNode for False | here |
|
||||
| make_request.py:34:1:34:45 | ControlFlowNode for Attribute() | This request may run without certificate validation because it is $@. | make_request.py:34:44:34:44 | ControlFlowNode for IntegerLiteral | disabled here | make_request.py:34:44:34:44 | ControlFlowNode for IntegerLiteral | here |
|
||||
| make_request.py:41:1:41:26 | ControlFlowNode for Attribute() | This request may run without certificate validation because it is $@. | make_request.py:41:21:41:25 | ControlFlowNode for False | disabled here | make_request.py:41:21:41:25 | ControlFlowNode for False | here |
|
||||
|
||||
@@ -36,3 +36,6 @@ requests.post('https://semmle.com', verify=0) # BAD
|
||||
# requests treat `None` as default value, which means it is turned on
|
||||
requests.get('https://semmle.com') # OK
|
||||
requests.get('https://semmle.com', verify=None) # OK
|
||||
|
||||
s = requests.Session()
|
||||
s.get("url", verify=False) # BAD
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user