TidGi-Desktop/.github/codeql/template-string-injection.ql
2025-10-24 15:07:58 +08:00

96 lines
2.9 KiB
Text

/**
* @name Template string injection in code execution
* @description Detects user input flowing into template strings that are passed to code execution functions
* @kind path-problem
* @problem.severity error
* @security-severity 9.3
* @precision high
* @id tidgi/template-string-injection
* @tags security
* external/cwe/cwe-094
* external/cwe/cwe-095
*/
import javascript
import DataFlow::PathGraph
/**
* A call to a function that executes code dynamically
*/
class CodeExecutionCall extends DataFlow::CallNode {
CodeExecutionCall() {
// Direct code execution
this.getCalleeName() = ["eval", "Function"] or
// Electron-specific code execution
this.getCalleeName() = "executeJavaScript" or
// VM module code execution
this.(DataFlow::MethodCallNode).getMethodName() = ["runInContext", "runInNewContext", "runInThisContext"]
}
}
/**
* A template literal that contains potentially tainted elements
*/
class TaintedTemplateLiteral extends DataFlow::Node {
TemplateLiteral literal;
TaintedTemplateLiteral() {
this.asExpr() = literal
}
TemplateLiteral getLiteral() {
result = literal
}
}
/**
* Configuration for tracking tainted data flow into template literals used in code execution
*/
class TemplateStringInjectionConfig extends TaintTracking::Configuration {
TemplateStringInjectionConfig() {
this = "TemplateStringInjectionConfig"
}
override predicate isSource(DataFlow::Node source) {
// Any parameter or property access could be user-controlled
source.asExpr() instanceof Parameter or
source instanceof DataFlow::PropRead or
// IPC sources in Electron
exists(DataFlow::CallNode call |
call.getCalleeName() = ["on", "handle", "once"] and
source = call.getCallback(0).getParameter(1)
)
}
override predicate isSink(DataFlow::Node sink) {
// Template literal elements that flow into code execution
exists(CodeExecutionCall exec, TaintedTemplateLiteral tl |
exec.getAnArgument() = tl and
exists(Expr element |
element = tl.getLiteral().getAnElement() and
sink.asExpr() = element
)
)
}
override predicate isSanitizer(DataFlow::Node node) {
// JSON.stringify is a safe sanitizer
exists(DataFlow::CallNode call |
call = DataFlow::globalVarRef("JSON").getAMemberCall("stringify") and
node = call
)
}
}
from TemplateStringInjectionConfig config, DataFlow::PathNode source, DataFlow::PathNode sink,
CodeExecutionCall exec, TaintedTemplateLiteral tl
where
config.hasFlowPath(source, sink) and
exec.getAnArgument() = tl and
exists(Expr element |
element = tl.getLiteral().getAnElement() and
sink.getNode().asExpr() = element
)
select exec, source, sink,
"Potential code injection: user input $@ flows into template string passed to " + exec.getCalleeName(),
source.getNode(), "here"