test: codeql for wikioperation

This commit is contained in:
lin onetwo 2025-10-24 15:07:11 +08:00
parent 47a0c93cfe
commit 944cf6f9cc
6 changed files with 302 additions and 0 deletions

27
.github/codeql/codeql-config.yml vendored Normal file
View file

@ -0,0 +1,27 @@
name: "TidGi CodeQL Security Analysis"
queries:
# Include default security queries
- uses: security-extended
# Include custom queries for Electron-specific vulnerabilities
- uses: ./.github/codeql/template-string-injection.ql
- uses: ./.github/codeql/electron-executejavascript.ql
- uses: ./.github/codeql/new-function-injection.ql
# Paths to analyze
paths:
- src
- scripts
- features
# Paths to ignore
paths-ignore:
- node_modules
- out
- dist
- coverage
- '**/__tests__/**'
- '**/*.test.ts'
- '**/*.test.tsx'
- 'userData-test'
- 'userData-dev'

View file

@ -0,0 +1,83 @@
/**
* @name Unsafe use of webFrame.executeJavaScript with user input
* @description Detects Electron webFrame.executeJavaScript calls with potentially tainted template strings
* @kind path-problem
* @problem.severity error
* @security-severity 9.0
* @precision high
* @id tidgi/electron-execute-javascript-injection
* @tags security
* electron
* external/cwe/cwe-094
*/
import javascript
import DataFlow::PathGraph
/**
* A call to webFrame.executeJavaScript
*/
class ExecuteJavaScriptCall extends DataFlow::MethodCallNode {
ExecuteJavaScriptCall() {
this.getMethodName() = "executeJavaScript" and
(
this.getReceiver().(DataFlow::PropRead).getPropertyName() = "webFrame" or
this.getReceiver().asExpr().(Identifier).getName() = "webFrame"
)
}
}
/**
* Configuration for tracking unsafe executeJavaScript usage
*/
class ExecuteJavaScriptInjectionConfig extends TaintTracking::Configuration {
ExecuteJavaScriptInjectionConfig() {
this = "ExecuteJavaScriptInjectionConfig"
}
override predicate isSource(DataFlow::Node source) {
// Function parameters
source.asExpr() instanceof Parameter or
// Object property access
source instanceof DataFlow::PropRead or
// Deep link handlers
exists(DataFlow::CallNode call |
(call.getCalleeName() = "on" or call.getCalleeName() = "handle") and
call.getArgument(0).getStringValue() = ["open-url", "second-instance"] and
source = call.getCallback(1).getParameter([0..2])
)
}
override predicate isSink(DataFlow::Node sink) {
exists(ExecuteJavaScriptCall exec |
sink = exec.getArgument(0)
)
}
override predicate isSanitizer(DataFlow::Node node) {
// JSON.stringify sanitizes the input
exists(DataFlow::CallNode call |
call = DataFlow::globalVarRef("JSON").getAMemberCall("stringify") and
node = call
) or
// Explicit type checks
exists(DataFlow::CallNode call |
call.getCalleeName() = ["isString", "isNumber", "isBoolean"] and
node = call
)
}
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
// Template string interpolation is a taint step
exists(TemplateLiteral tl |
pred.asExpr() = tl.getAnElement() and
succ.asExpr() = tl
)
}
}
from ExecuteJavaScriptInjectionConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink,
"Potential code injection in webFrame.executeJavaScript: user input $@ flows into executed code without proper sanitization",
source.getNode(), "here"

View file

@ -0,0 +1,83 @@
/**
* @name Unsafe use of Function constructor with user input
* @description Detects Function constructor calls with potentially tainted template strings
* @kind path-problem
* @problem.severity error
* @security-severity 9.0
* @precision high
* @id tidgi/new-function-injection
* @tags security
* external/cwe/cwe-094
*/
import javascript
import DataFlow::PathGraph
/**
* A call to the Function constructor
*/
class FunctionConstructorCall extends DataFlow::NewNode {
FunctionConstructorCall() {
this.getCalleeName() = "Function"
}
}
/**
* Configuration for tracking unsafe Function constructor usage
*/
class FunctionConstructorInjectionConfig extends TaintTracking::Configuration {
FunctionConstructorInjectionConfig() {
this = "FunctionConstructorInjectionConfig"
}
override predicate isSource(DataFlow::Node source) {
// Function parameters
source.asExpr() instanceof Parameter or
// Object property access
source instanceof DataFlow::PropRead or
// IPC message handlers
exists(DataFlow::CallNode call |
(call.getCalleeName() = "on" or call.getCalleeName() = "handle") and
source = call.getCallback([0..1]).getParameter([0..2])
)
}
override predicate isSink(DataFlow::Node sink) {
exists(FunctionConstructorCall fnCall |
sink = fnCall.getAnArgument()
)
}
override predicate isSanitizer(DataFlow::Node node) {
// JSON.parse/stringify are safe
exists(DataFlow::CallNode call |
call = DataFlow::globalVarRef("JSON").getAMemberCall(["stringify", "parse"]) and
node = call
) or
// Whitelist validation
exists(DataFlow::MethodCallNode test |
test.getMethodName() = "test" and
test.getReceiver().asExpr() instanceof RegExpLiteral and
node = test
)
}
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
// Template string interpolation
exists(TemplateLiteral tl |
pred.asExpr() = tl.getAnElement() and
succ.asExpr() = tl
) or
// String concatenation
exists(AddExpr add |
pred.asExpr() = add.getAnOperand() and
succ.asExpr() = add
)
}
}
from FunctionConstructorInjectionConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink,
"Potential code injection via Function constructor: user input $@ flows into dynamically created function",
source.getNode(), "here"

4
.github/codeql/qlpack.yml vendored Normal file
View file

@ -0,0 +1,4 @@
name: tidgi/security-queries
version: 1.0.0
libraryPathDependencies:
- codeql/javascript-all

View file

@ -0,0 +1,96 @@
/**
* @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"

View file

@ -63,6 +63,10 @@ jobs:
codeql:
name: CodeQL Analysis
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@v5
@ -71,5 +75,10 @@ jobs:
uses: github/codeql-action/init@v3
with:
languages: javascript-typescript
# Use custom CodeQL configuration with Electron-specific queries
config-file: ./.github/codeql/codeql-config.yml
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:javascript-typescript"