Merge branch 'experimental-manually-check-request-verb' of https://github.com/thiggy1342/codeql into experimental-manually-check-request-verb

This commit is contained in:
thiggy1342
2022-06-21 21:27:39 +00:00
committed by GitHub
526 changed files with 8923 additions and 1562 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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",

View File

@@ -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
}

View File

@@ -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 |

View File

@@ -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

View File

@@ -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. |

View File

@@ -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 |

View File

@@ -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)... |

View File

@@ -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

View File

@@ -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 {

View File

@@ -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' ]

View File

@@ -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)

View File

@@ -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) ||

View File

@@ -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()

View 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) }
}

View File

@@ -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.
*

View File

@@ -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"

View File

@@ -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?"

View File

@@ -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 + "."

View File

@@ -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()
}
}

View File

@@ -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

View File

@@ -0,0 +1 @@
fun usesEnum(e: Enum<*>) = e.ordinal.toString() + e.name

View 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 |

View File

@@ -0,0 +1,5 @@
import java
from Method m
where m.getDeclaringType().getName().matches("Enum%")
select m.getName()

View File

@@ -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

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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();
}
}

View File

@@ -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 |

View File

@@ -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()

View File

@@ -0,0 +1,5 @@
import java
from Method m
where m.getDeclaringType().getName() = ["Number", "Byte"]
select m.getDeclaringType().getQualifiedName(), m.toString()

View File

@@ -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; }
}

View File

@@ -0,0 +1,3 @@
| get |
| remove |
| size |

View File

@@ -0,0 +1,2 @@
fun f(l: MyList<String>) = l.get(0)

View File

@@ -0,0 +1,5 @@
import java
from Method m
where m.getDeclaringType().getName().matches("MyList%")
select m.toString()

View File

@@ -0,0 +1,2 @@
| test.kt:1:100:1:109 | mutableIterator(...) | mutableIterator |
| test.kt:3:73:3:82 | iterator(...) | iterator |

View File

@@ -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()

View File

@@ -0,0 +1,4 @@
import java
from MethodAccess ma
select ma, ma.getCallee().toString()

View File

@@ -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. |

View File

@@ -4,3 +4,4 @@
*/
import semmle.javascript.dataflow.DataFlow::DataFlow as DataFlow
import semmle.javascript.security.CryptoAlgorithms as CryptoAlgorithms

View File

@@ -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" }
}
}

View File

@@ -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) }
}
/**

View File

@@ -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"] }

View 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"

View File

@@ -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
}

View File

@@ -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) }
}
/**

View File

@@ -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"] }

View File

@@ -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
}
}
}

View File

@@ -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() }
}
}

View File

@@ -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() }
}
}
}

View File

@@ -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
)
}
}
}

View File

@@ -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. */

View File

@@ -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() }
}
}

View File

@@ -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() }
}
/**

View File

@@ -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()
}
}

View File

@@ -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()
}
}

View File

@@ -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
)
}
}
}

View File

@@ -4,3 +4,4 @@
*/
import semmle.python.dataflow.new.DataFlow
import semmle.python.concepts.CryptoAlgorithms as CryptoAlgorithms

View File

@@ -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" }
}
}

View File

@@ -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"

View File

@@ -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."

View File

@@ -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 + "."

View File

@@ -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" }
}

View File

@@ -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.

View File

@@ -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`.

View 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"

View File

@@ -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()] }
}

View File

@@ -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

View File

@@ -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")]
}
}
}
}
}

View 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")
}
}
}

View File

@@ -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") }
}
}

View 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")]
}
}
}

View File

@@ -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)
)
}
}

View File

@@ -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"
)
)
}

View File

@@ -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 |

View File

@@ -0,0 +1 @@
experimental/Security/CWE-079/ReflectedXSS.ql

View File

@@ -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"))

View File

@@ -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)

View File

@@ -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())

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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]

View File

@@ -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]

View File

@@ -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]

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 |

View File

@@ -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