Skip to content

Commit

Permalink
add Plugin to run rules and small formatting changes
Browse files Browse the repository at this point in the history
  • Loading branch information
MariusAlbrecht committed Oct 6, 2024
1 parent 77862f8 commit a09f0ec
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class IncrementIntegerOverflow : Rule {
override val cweId: String = "128"
override val shortDescription =
"Detects (post- or prefix) unary increments that may cause the their target to " +
"overflow"
"overflow"
override val level = Rule.Level.Error
override val message = "Increment may cause overflow"

Expand All @@ -49,21 +49,21 @@ class IncrementIntegerOverflow : Rule {
{
val max = max(it.input)
(max eq maxSizeOfType(it.input.type)) or
when (max.value) {
is Long -> max as QueryTree<Long> eq
const((2L shl (it.input.type as NumericType).bitWidth!!) - 1)

is Int -> max as QueryTree<Int> eq
const((2 shl (it.input.type as NumericType).bitWidth!!) - 1)

is Float -> max as QueryTree<Float> eq
const(2.0f.pow((it.input.type as NumericType).bitWidth!!) - 1)

is Double -> max as QueryTree<Double> eq
const(2.0.pow((it.input.type as NumericType).bitWidth!!) - 1)

else -> const(false)
}
when (max.value) {
is Long ->
max as QueryTree<Long> eq
const((2L shl (it.input.type as NumericType).bitWidth!!) - 1)
is Int ->
max as QueryTree<Int> eq
const((2 shl (it.input.type as NumericType).bitWidth!!) - 1)
is Float ->
max as QueryTree<Float> eq
const(2.0f.pow((it.input.type as NumericType).bitWidth!!) - 1)
is Double ->
max as QueryTree<Double> eq
const(2.0.pow((it.input.type as NumericType).bitWidth!!) - 1)
else -> const(false)
}
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ package de.fraunhofer.aisec.cpg.rules

import de.fraunhofer.aisec.cpg.TranslationResult
import de.fraunhofer.aisec.cpg.graph.HasBase
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Literal
import de.fraunhofer.aisec.cpg.query.*

class NullPointerDereference : Rule {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,16 @@ package de.fraunhofer.aisec.cpg.rules
import de.fraunhofer.aisec.cpg.TranslationResult
import de.fraunhofer.aisec.cpg.graph.statements.expressions.BinaryOperator
import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression
import de.fraunhofer.aisec.cpg.query.*
import de.fraunhofer.aisec.cpg.rules.Util

class PathTraversalViaUserInputConcatenationRule : Rule {
override var queryResult: QueryTree<*>? = null

override val id = "PTR-001"
override val name = "Path Traversal via User Input Concatenation"
override val cweId = "22"
override val shortDescription = "User input is concatenated with file paths without proper validation"
override val shortDescription =
"User input is concatenated with file paths without proper validation"
override val mdShortDescription = null
override val level = Rule.Level.Warning
override val message =
Expand All @@ -57,23 +56,24 @@ class PathTraversalViaUserInputConcatenationRule : Rule {
from = outer,
predicate = { inner ->
inner is CallExpression &&
inner.name.localName.contains(concatFunctionsRegex) &&
inner.arguments.any { Util.isPathLike(it) } ||
inner is BinaryOperator && inner.operatorCode == "+" ||
inner is BinaryOperator && inner.operatorCode == "+="
inner.name.localName.contains(concatFunctionsRegex) &&
inner.arguments.any { Util.isPathLike(it) } ||
inner is BinaryOperator && inner.operatorCode == "+" ||
inner is BinaryOperator && inner.operatorCode == "+="
},
collectFailedPaths = false,
findAllPossiblePaths = true
) and not( // and doesn't reach validation
dataFlow(
from = outer,
predicate = {
it is CallExpression && Util.isValidationFunction(it)
},
collectFailedPaths = false,
findAllPossiblePaths = true
) and
not( // and doesn't reach validation
dataFlow(
from = outer,
predicate = {
it is CallExpression && Util.isValidationFunction(it)
},
collectFailedPaths = false,
findAllPossiblePaths = true
)
)
)
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ class UseAfterFree : Rule {
{ outer ->
executionPath(outer) { inner ->
(outer.arguments[0] as? Reference)?.refersTo == // free argument
(inner as? Reference)?.refersTo // reference to free argument after the free
(inner as? Reference)
?.refersTo // reference to free argument after the free
}
}
)
Expand Down
49 changes: 39 additions & 10 deletions cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/rules/Util.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
/*
* Copyright (c) 2024, Fraunhofer AISEC. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $$$$$$\ $$$$$$$\ $$$$$$\
* $$ __$$\ $$ __$$\ $$ __$$\
* $$ / \__|$$ | $$ |$$ / \__|
* $$ | $$$$$$$ |$$ |$$$$\
* $$ | $$ ____/ $$ |\_$$ |
* $$ | $$\ $$ | $$ | $$ |
* \$$$$$ |$$ | \$$$$$ |
* \______/ \__| \______/
*
*/
package de.fraunhofer.aisec.cpg.rules

import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression
Expand All @@ -7,23 +32,27 @@ class Util {
companion object {
fun isPathLike(arg: Expression): Boolean {
return arg.type.name.localName.contains(Regex("str|char", RegexOption.IGNORE_CASE)) &&
(arg.name.localName.contains(
(arg.name.localName.contains(
Regex("path|file|dir|directory|url|uri", RegexOption.IGNORE_CASE)
) ||
arg.code?.contains(
Regex("path|file|dir|directory|url|uri", RegexOption.IGNORE_CASE)
) ||
arg.code?.contains(
Regex("path|file|dir|directory|url|uri", RegexOption.IGNORE_CASE)
) ?: false ||
arg.code?.contains('/') ?: false)
) ?: false ||
arg.code?.contains('/') ?: false)
}

fun isUserInput(arg: CallExpression): Boolean {
val userInputRegex = Regex("user|input|param|arg|argument|request|query", RegexOption.IGNORE_CASE)
return arg.name.localName.contains(userInputRegex) || arg.code?.contains(userInputRegex) ?: false
val userInputRegex =
Regex("user|input|param|arg|argument|request|query", RegexOption.IGNORE_CASE)
return arg.name.localName.contains(userInputRegex) ||
arg.code?.contains(userInputRegex) ?: false
}

fun isValidationFunction(arg: CallExpression): Boolean {
val validationFunctionRegex = Regex("val|validate|check|san|sanitize|clean", RegexOption.IGNORE_CASE)
return arg.name.localName.contains(validationFunctionRegex) || arg.code?.contains(validationFunctionRegex) ?: false
val validationFunctionRegex =
Regex("val|validate|check|san|sanitize|clean", RegexOption.IGNORE_CASE)
return arg.name.localName.contains(validationFunctionRegex) ||
arg.code?.contains(validationFunctionRegex) ?: false
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright (c) 2021, Fraunhofer AISEC. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $$$$$$\ $$$$$$$\ $$$$$$\
* $$ __$$\ $$ __$$\ $$ __$$\
* $$ / \__|$$ | $$ |$$ / \__|
* $$ | $$$$$$$ |$$ |$$$$\
* $$ | $$ ____/ $$ |\_$$ |
* $$ | $$\ $$ | $$ | $$ |
* \$$$$$ |$$ | \$$$$$ |
* \______/ \__| \______/
*
*/
package de.fraunhofer.aisec.cpg.console

import org.jetbrains.kotlinx.ki.shell.BaseCommand
import org.jetbrains.kotlinx.ki.shell.Command
import org.jetbrains.kotlinx.ki.shell.Plugin
import org.jetbrains.kotlinx.ki.shell.Shell
import org.jetbrains.kotlinx.ki.shell.configuration.ReplConfiguration

class RunRulePlugin : Plugin {
inner class Load(conf: ReplConfiguration) : BaseCommand() {
override val name: String by conf.get(default = "runRule")
override val short: String by conf.get(default = "rr")
override val description: String =
"runs a rule. Requires `result` to be set, e.g. by running the :translate " + "command."

override val params = "<rule[ rule]>"

override fun execute(line: String): Command.Result {
val ruletoRun = line.split(" ")[1] // [":r", "<rule>"]
return Command.Result.RunSnippets(
listOf(
// import
"import de.fraunhofer.aisec.cpg.rules.$ruletoRun",
"import de.fraunhofer.aisec.cpg.query.SarifReporter",
// run it
"val rule = $ruletoRun()",
"rule.run(result)",
"rules.add(rule)",
)
)
}
}

lateinit var repl: Shell

override fun init(repl: Shell, config: ReplConfiguration) {
this.repl = repl

repl.registerCommand(Load(config))
}

override fun cleanUp() {
// nothing to do
}
}

0 comments on commit a09f0ec

Please sign in to comment.