From 944cf6f9cc4e69c54d36d29a1eea6fcc152eb017 Mon Sep 17 00:00:00 2001 From: lin onetwo Date: Fri, 24 Oct 2025 15:07:11 +0800 Subject: [PATCH] test: codeql for wikioperation --- .github/codeql/codeql-config.yml | 27 ++++++ .github/codeql/electron-executejavascript.ql | 83 +++++++++++++++++ .github/codeql/new-function-injection.ql | 83 +++++++++++++++++ .github/codeql/qlpack.yml | 4 + .github/codeql/template-string-injection.ql | 96 ++++++++++++++++++++ .github/workflows/test.yml | 9 ++ 6 files changed, 302 insertions(+) create mode 100644 .github/codeql/codeql-config.yml create mode 100644 .github/codeql/electron-executejavascript.ql create mode 100644 .github/codeql/new-function-injection.ql create mode 100644 .github/codeql/qlpack.yml create mode 100644 .github/codeql/template-string-injection.ql diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml new file mode 100644 index 00000000..9e4c570e --- /dev/null +++ b/.github/codeql/codeql-config.yml @@ -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' diff --git a/.github/codeql/electron-executejavascript.ql b/.github/codeql/electron-executejavascript.ql new file mode 100644 index 00000000..bcb11b3d --- /dev/null +++ b/.github/codeql/electron-executejavascript.ql @@ -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" diff --git a/.github/codeql/new-function-injection.ql b/.github/codeql/new-function-injection.ql new file mode 100644 index 00000000..fb4e03ed --- /dev/null +++ b/.github/codeql/new-function-injection.ql @@ -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" diff --git a/.github/codeql/qlpack.yml b/.github/codeql/qlpack.yml new file mode 100644 index 00000000..b45383dc --- /dev/null +++ b/.github/codeql/qlpack.yml @@ -0,0 +1,4 @@ +name: tidgi/security-queries +version: 1.0.0 +libraryPathDependencies: + - codeql/javascript-all diff --git a/.github/codeql/template-string-injection.ql b/.github/codeql/template-string-injection.ql new file mode 100644 index 00000000..ac44ab17 --- /dev/null +++ b/.github/codeql/template-string-injection.ql @@ -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" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8ec0de39..534617cd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -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"