-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Description
Hello CodeQL team,
I encountered an issue where CodeQL fails to resolve a method call correctly when analyzing decompiled/obfuscated Java code. The issue arises when a package name conflicts with a class name in the parent package.
In this scenario, CodeQL seems to prioritize resolving the name as a class, causing the subsequent path to be invalid, resulting in an in the AST and a broken data flow.
Reproduction Code (Synthetic Example):
Since I cannot share the proprietary code, I have constructed a minimal reproduction case that mimics the structure:
Context: A class named a exists in package com.demo.util.
Context: A package named a also exists under com.demo.util.
Target: We are trying to call a static method g() in class com.demo.util.a.a.
src/
├── com/demo/util/a.java <-- The class "a"
└── com/demo/util/a/a.java <-- The package "a" containing class "a"
JavaCode:
// File: src/com/demo/util/a.java package com.demo.util; public class a { // This class is empty or does NOT have method g() } // File: src/com/demo/util/a/a.java package com.demo.util.a; public class a { public static String g(String input) { return input; } }
My Vul Code is :
@org.springframework.web.bind.annotation.PostMapping({"/testConnection"}) @org.jeecg.authz.annotation.RequiresPermissions({"drag:datasource:testConnection"}) public org.jeecg.modules.drag.config.common.Result a(@org.springframework.web.bind.annotation.RequestBody org.jeecg.modules.drag.vo.DynamicDataSourceVo dynamicDataSourceVo) throws java.sql.SQLException { java.sql.Connection connection = null; java.lang.String string = dynamicDataSourceVo.toString(); java.lang.Object objA = this.localCache.a(string); if (org.jeecg.modules.drag.util.h.d(objA)) { int iIntValue = org.jeecg.modules.drag.util.h.e(objA).intValue(); if (iIntValue >= 3) { return org.jeecg.modules.drag.config.common.Result.error("数据源已连接错误3次以上,请检查配置信息!"); } if (iIntValue == 0) { return org.jeecg.modules.drag.config.common.Result.OK("数据库连接成功", true); } } else { this.localCache.a(string, 0, org.jeecg.modules.jmreport.common.util.DateUtils.k); } try { try { try { java.lang.Class.forName(dynamicDataSourceVo.getDbDriver()); java.sql.DriverManager.setLoginTimeout(60); java.sql.Connection connection2 = java.sql.DriverManager.getConnection(org.jeecg.modules.drag.util.a.a.g(dynamicDataSourceVo.getDbUrl()), dynamicDataSourceVo.getDbUsername(), dynamicDataSourceVo.getDbPassword()); if (connection2 != null) { org.jeecg.modules.drag.config.common.Result resultOK = org.jeecg.modules.drag.config.common.Result.OK("数据库连接成功", true); if (connection2 != null) { try { if (!connection2.isClosed()) { connection2.close(); } } catch (java.sql.SQLException e) { a.error(e.toString(), e); } } return resultOK; } this.localCache.a(string, 1); org.jeecg.modules.drag.config.common.Result resultOK2 = org.jeecg.modules.drag.config.common.Result.OK("failed:unknown", true); if (connection2 != null) { try { if (!connection2.isClosed()) { connection2.close(); } } catch (java.sql.SQLException e2) { a.error(e2.toString(), e2); } } return resultOK2; } catch (java.lang.Exception e3) { a.error(e3.toString(), e3); this.localCache.a(string, 1); org.jeecg.modules.drag.config.common.Result resultError = org.jeecg.modules.drag.config.common.Result.error("failed:" + e3.getMessage()); if (0 != 0) { try { if (!connection.isClosed()) { connection.close(); } } catch (java.sql.SQLException e4) { a.error(e4.toString(), e4); return resultError; } } return resultError; } } catch (java.lang.ClassNotFoundException e5) { a.error(e5.toString(), e5); this.localCache.a(string, 1); org.jeecg.modules.drag.config.common.Result resultError2 = org.jeecg.modules.drag.config.common.Result.error("failed:not exist"); if (0 != 0) { try { if (!connection.isClosed()) { connection.close(); } } catch (java.sql.SQLException e6) { a.error(e6.toString(), e6); return resultError2; } } return resultError2; } } catch (java.lang.Throwable th) { if (0 != 0) { try { if (!connection.isClosed()) { connection.close(); } } catch (java.sql.SQLException e7) { a.error(e7.toString(), e7); throw th; } } throw th; } }
and my QL code is:
/** * @name Find JDBC Connection Calls * @description Find all usages of DriverManager.getConnection * @kind problem * @problem.severity warning * @id java/find-jdbc-connection */ import java // 1. 定义这个目标方法 (和你写的一样,稍微优化了精准度) class JDBCConnectionMethod extends Method { JDBCConnectionMethod() { // DriverManager 是类名,不需要 getAnAncestor,直接用 hasQualifiedName 更快更准 this.getDeclaringType().hasQualifiedName("java.sql", "DriverManager") and this.hasName("getConnection") } } // 2. 核心查询逻辑 from MethodCall call where // 限制这个调用必须是调用了我们上面定义的那个方法 call.getMethod() instanceof JDBCConnectionMethod select call, "发现 DriverManager.getConnection 的调用,第0个参数是: " + call.getArgument(0) // /** // * @name Check for Ghost Class // * @kind problem // * @id java/check-ghost-class // */ // import java // from Class c // where c.hasQualifiedName("org.jeecg.modules.drag.util.a", "a") // select c, "文件路径: " + c.getFile().getAbsolutePath(), "代码行数: " + c.getNumberOfLinesOfCode()
Observed Behavior (AST View):
When using "CodeQL: View AST" in VS Code on the line com.demo.util.a.a.g(...):
The method call is marked as .
The qualifier com.demo.util.a resolves to the Class com.demo.util.a instead of the Package.
Since Class com.demo.util.a does not contain method a or g, the resolution fails.
Expected Behavior:
CodeQL should correctly identify that com.demo.util.a.a refers to the class a inside package com.demo.util.a, even if a class named com.demo.util.a exists.
Build Environment:
Build Mode: codeql database create --command="autobuild -none" ... (Analyzing source code directly)
CodeQL CLI Version: [这里填你的版本,例如 2.23.8]
OS: [/ macOS Sonoma]
Impact: This causes severe data flow breakage in TaintTracking for obfuscated applications, as the source-to-sink path is interrupted at the method call.
thank you sir :) I await your reply