From 6700c62243717c4de9b871a84048d22caf24a9f7 Mon Sep 17 00:00:00 2001 From: lin onetwo Date: Fri, 24 Oct 2025 16:40:32 +0800 Subject: [PATCH] test: codeql for wikioperation (#647) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test: codeql for wikioperation * test: codeql not find suite * Move CodeQL analysis to dedicated workflow Extracted the CodeQL security analysis job from test.yml into a new codeql-analysis.yml workflow file for better separation of concerns and maintainability. * Update codeql-analysis.yml * de * 。 --- .github/codeql/codeql-config.yml | 18 ++++ .github/codeql/codeql-pack.lock.yml | 30 ++++++ .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/codeql-analysis.yml | 42 +++++++++ .github/workflows/test.yml | 14 --- .gitignore | 3 +- 9 files changed, 358 insertions(+), 15 deletions(-) create mode 100644 .github/codeql/codeql-config.yml create mode 100644 .github/codeql/codeql-pack.lock.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 create mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml new file mode 100644 index 00000000..9ecc34de --- /dev/null +++ b/.github/codeql/codeql-config.yml @@ -0,0 +1,18 @@ +name: "TidGi CodeQL Security Analysis" + +queries: + - uses: security-extended + +# Paths to analyze (only source code) +paths: + - src + - scripts + +# Paths to ignore within the analyzed paths +# (Excludes test files and mock data from security analysis) +paths-ignore: + - '**/__tests__/**' + - '**/__mocks__/**' + - '**/*.test.ts' + - '**/*.test.tsx' + - '**/*.spec.ts' diff --git a/.github/codeql/codeql-pack.lock.yml b/.github/codeql/codeql-pack.lock.yml new file mode 100644 index 00000000..602c0a96 --- /dev/null +++ b/.github/codeql/codeql-pack.lock.yml @@ -0,0 +1,30 @@ +--- +lockVersion: 1.0.0 +dependencies: + codeql/concepts: + version: 0.0.7 + codeql/controlflow: + version: 2.0.17 + codeql/dataflow: + version: 2.0.17 + codeql/javascript-all: + version: 2.6.13 + codeql/mad: + version: 1.0.33 + codeql/regex: + version: 1.0.33 + codeql/ssa: + version: 2.0.9 + codeql/threat-models: + version: 1.0.33 + codeql/tutorial: + version: 1.0.33 + codeql/typetracking: + version: 2.0.17 + codeql/util: + version: 2.0.20 + codeql/xml: + version: 1.0.33 + codeql/yaml: + version: 1.0.33 +compiled: false 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..91116d42 --- /dev/null +++ b/.github/codeql/qlpack.yml @@ -0,0 +1,4 @@ +name: tidgi/security-queries +version: 1.0.0 +dependencies: + 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/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000..48b1d446 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,42 @@ +name: "CodeQL Security Analysis" + +on: + push: + tags: + - "v*.*.*" + paths-ignore: + - "README.md" + - "docs/**" + - ".vscode" + pull_request: + branches: + - master + paths-ignore: + - "docs/**" + - "README.md" + - ".vscode" + +jobs: + analyze: + name: Analyze JavaScript/TypeScript + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: javascript-typescript + config-file: ./.github/codeql/codeql-config.yml + queries: +./.github/codeql + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:javascript-typescript" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8ec0de39..4ee74c5c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -59,17 +59,3 @@ jobs: userData-test/settings/ retention-days: 7 continue-on-error: true - - codeql: - name: CodeQL Analysis - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v5 - - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: javascript-typescript - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 diff --git a/.gitignore b/.gitignore index 1227140a..e56e53d5 100644 --- a/.gitignore +++ b/.gitignore @@ -72,4 +72,5 @@ userData-test/ wiki-dev/ wiki-test/ *.tsbuildinfo -tsconfig.test.json.tsbuildinfo \ No newline at end of file +tsconfig.test.json.tsbuildinfo +/tidgi-codeql-db