diff --git a/codeql-custom-queries-java/android/avoid-android-sdk-location.ql b/codeql-custom-queries-java/android/avoid-android-sdk-location.ql index b9be9f6..b43b111 100644 --- a/codeql-custom-queries-java/android/avoid-android-sdk-location.ql +++ b/codeql-custom-queries-java/android/avoid-android-sdk-location.ql @@ -12,8 +12,6 @@ import java -import java - from RefType t, Variable v where v.getType() = t and diff --git a/codeql-custom-queries-java/android/avoid-brightness-override.expected b/codeql-custom-queries-java/android/avoid-brightness-override.expected new file mode 100644 index 0000000..6829855 --- /dev/null +++ b/codeql-custom-queries-java/android/avoid-brightness-override.expected @@ -0,0 +1,3 @@ +| avoid-brightness-override.java:17:9:17:31 | params.screenBrightness | Avoid setting screenBrightness to BRIGHTNESS_OVERRIDE_FULL. This disables Android's adaptive brightness feature which was introduced to improve battery life. | +| avoid-brightness-override.java:23:9:23:31 | params.screenBrightness | Avoid setting screenBrightness to BRIGHTNESS_OVERRIDE_FULL. This disables Android's adaptive brightness feature which was introduced to improve battery life. | +| avoid-brightness-override.java:29:13:29:35 | params.screenBrightness | Avoid setting screenBrightness to BRIGHTNESS_OVERRIDE_FULL. This disables Android's adaptive brightness feature which was introduced to improve battery life. | diff --git a/codeql-custom-queries-java/android/avoid-brightness-override.java b/codeql-custom-queries-java/android/avoid-brightness-override.java new file mode 100644 index 0000000..1420a06 --- /dev/null +++ b/codeql-custom-queries-java/android/avoid-brightness-override.java @@ -0,0 +1,51 @@ +// WindowManager et WindowManager.LayoutParams sont déjà déclarés dans avoid-keep-screen-on.java +// avec seulement FLAG_KEEP_SCREEN_ON — on ne peut pas les redéclarer ni les modifier. +// On utilise une classe proxy BrightnessParams qui simule WindowManager.LayoutParams +// pour les champs screenBrightness et BRIGHTNESS_OVERRIDE_FULL. + +class BrightnessParams { + float screenBrightness; + static final float BRIGHTNESS_OVERRIDE_FULL = 1.0f; + static final float BRIGHTNESS_OVERRIDE_NONE = -1.0f; +} + +// 🚫 Noncompliant - screenBrightness set to BRIGHTNESS_OVERRIDE_FULL +class NoncompliantBrightnessOverride { + + void disableAdaptiveBrightness() { + BrightnessParams params = new BrightnessParams(); + params.screenBrightness = BrightnessParams.BRIGHTNESS_OVERRIDE_FULL; // $ Alert + } + + void disableAdaptiveBrightnessWithVar() { + float override = BrightnessParams.BRIGHTNESS_OVERRIDE_FULL; + BrightnessParams params = new BrightnessParams(); + params.screenBrightness = override; // $ Alert + } + + void disableInLoop() { + for (int i = 0; i < 3; i++) { + BrightnessParams params = new BrightnessParams(); + params.screenBrightness = BrightnessParams.BRIGHTNESS_OVERRIDE_FULL; // $ Alert + } + } +} + +// ✅ Compliant - screenBrightness not set to BRIGHTNESS_OVERRIDE_FULL +class CompliantBrightnessUsage { + + void useAdaptiveBrightness() { + BrightnessParams params = new BrightnessParams(); + params.screenBrightness = BrightnessParams.BRIGHTNESS_OVERRIDE_NONE; + } + + void useCustomBrightness() { + BrightnessParams params = new BrightnessParams(); + params.screenBrightness = 0.5f; + } + + void noScreenBrightnessSet() { + BrightnessParams params = new BrightnessParams(); + int x = 42; + } +} \ No newline at end of file diff --git a/codeql-custom-queries-java/android/avoid-brightness-override.ql b/codeql-custom-queries-java/android/avoid-brightness-override.ql new file mode 100644 index 0000000..321a235 --- /dev/null +++ b/codeql-custom-queries-java/android/avoid-brightness-override.ql @@ -0,0 +1,24 @@ +/** + * @name Brightness Override + * @description Setting WindowManager.LayoutParams#screenBrightness to BRIGHTNESS_OVERRIDE_FULL + * disables the adaptive brightness feature introduced in Android 9. + * Adaptive brightness adjusts screen brightness based on environment light + * to improve battery life. Overriding it is a very bad idea. + * @kind problem + * @problem.severity warning + * @id java/android/avoid-brightness-override + * @tags android + * @tags java + */ + +import java + +from FieldWrite fw +where + fw.getField().getName() = "screenBrightness" and + exists(FieldRead fr | + fr.getField().getName() = "BRIGHTNESS_OVERRIDE_FULL" and + fw.getEnclosingCallable() = fr.getEnclosingCallable() + ) +select fw, + "Avoid setting screenBrightness to BRIGHTNESS_OVERRIDE_FULL. This disables Android's adaptive brightness feature which was introduced to improve battery life." \ No newline at end of file diff --git a/codeql-custom-queries-java/android/avoid-day-night-mode.expected b/codeql-custom-queries-java/android/avoid-day-night-mode.expected new file mode 100644 index 0000000..5ee9390 --- /dev/null +++ b/codeql-custom-queries-java/android/avoid-day-night-mode.expected @@ -0,0 +1,10 @@ +| avoid-day-night-mode.xml:5:5:5:65 | parent=Theme.AppCompat.Light | The theme 'AppTheme.Light' does not inherit from a DayNight theme. Use 'Theme.*.DayNight' as parent to support Android dark mode and reduce energy consumption on (AM)OLED screens. | +| avoid-day-night-mode.xml:9:5:9:58 | parent=Theme.AppCompat | The theme 'AppTheme.Dark' does not inherit from a DayNight theme. Use 'Theme.*.DayNight' as parent to support Android dark mode and reduce energy consumption on (AM)OLED screens. | +| avoid-day-night-mode.xml:13:5:13:71 | parent=Theme.MaterialComponents | The theme 'AppTheme.Material' does not inherit from a DayNight theme. Use 'Theme.*.DayNight' as parent to support Android dark mode and reduce energy consumption on (AM)OLED screens. | +| avoid-day-night-mode.xml:17:5:17:83 | parent=Theme.AppCompat.Light.DarkActionBar | The theme 'AppTheme.LightDark' does not inherit from a DayNight theme. Use 'Theme.*.DayNight' as parent to support Android dark mode and reduce energy consumption on (AM)OLED screens. | +| avoid-ligth-ui.xml:5:5:5:65 | parent=Theme.AppCompat.Light | The theme 'AppTheme.Light' does not inherit from a DayNight theme. Use 'Theme.*.DayNight' as parent to support Android dark mode and reduce energy consumption on (AM)OLED screens. | +| avoid-ligth-ui.xml:9:5:9:82 | parent=Theme.MaterialComponents.Light | The theme 'AppTheme.MaterialLight' does not inherit from a DayNight theme. Use 'Theme.*.DayNight' as parent to support Android dark mode and reduce energy consumption on (AM)OLED screens. | +| avoid-ligth-ui.xml:13:5:13:83 | parent=Theme.AppCompat.Light.DarkActionBar | The theme 'AppTheme.LightDark' does not inherit from a DayNight theme. Use 'Theme.*.DayNight' as parent to support Android dark mode and reduce energy consumption on (AM)OLED screens. | +| avoid-ligth-ui.xml:17:5:17:100 | parent=Theme.MaterialComponents.Light.DarkActionBar | The theme 'AppTheme.MaterialLightDark' does not inherit from a DayNight theme. Use 'Theme.*.DayNight' as parent to support Android dark mode and reduce energy consumption on (AM)OLED screens. | +| avoid-ligth-ui.xml:21:5:21:58 | parent=Theme.AppCompat | The theme 'AppTheme.Dark' does not inherit from a DayNight theme. Use 'Theme.*.DayNight' as parent to support Android dark mode and reduce energy consumption on (AM)OLED screens. | +| avoid-ligth-ui.xml:25:5:25:75 | parent=Theme.MaterialComponents | The theme 'AppTheme.MaterialDark' does not inherit from a DayNight theme. Use 'Theme.*.DayNight' as parent to support Android dark mode and reduce energy consumption on (AM)OLED screens. | diff --git a/codeql-custom-queries-java/android/avoid-day-night-mode.ql b/codeql-custom-queries-java/android/avoid-day-night-mode.ql new file mode 100644 index 0000000..7f1f2d9 --- /dev/null +++ b/codeql-custom-queries-java/android/avoid-day-night-mode.ql @@ -0,0 +1,26 @@ +/** + * @name Day Night Mode + * @description Dark theme is available in Android 10 (API level 29) and higher. + * Apps should support Dark theme by inheriting from a DayNight theme + * (parent="Theme.*.DayNight"). Failing to do so prevents the app from + * adapting to the user's system dark mode preference, increasing energy + * consumption on (AM)OLED screens. + * @kind problem + * @problem.severity warning + * @id java/android/avoid-day-night-mode + * @tags android + * @tags java + * + * @note Test contains those for the darktheme detection too + */ + +import semmle.code.xml.XML + +from XmlAttribute attr +where + attr.getName() = "parent" and + not attr.getValue().matches("%DayNight%") and + attr.getElement().getName() = "style" +select attr, + "The theme '" + attr.getElement().getAttribute("name").getValue() + + "' does not inherit from a DayNight theme. Use 'Theme.*.DayNight' as parent to support Android dark mode and reduce energy consumption on (AM)OLED screens." \ No newline at end of file diff --git a/codeql-custom-queries-java/android/avoid-day-night-mode.xml b/codeql-custom-queries-java/android/avoid-day-night-mode.xml new file mode 100644 index 0000000..421c3c8 --- /dev/null +++ b/codeql-custom-queries-java/android/avoid-day-night-mode.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/codeql-custom-queries-java/android/avoid-everlasting-service.expected b/codeql-custom-queries-java/android/avoid-everlasting-service.expected new file mode 100644 index 0000000..a0fee8f --- /dev/null +++ b/codeql-custom-queries-java/android/avoid-everlasting-service.expected @@ -0,0 +1,4 @@ +| avoid-everlasting-service.java:15:9:15:38 | startService(...) | startService() is called here without a corresponding stopService() or stopSelf() in the same method. This may keep the service alive indefinitely and cause uncontrolled energy leakage. | +| avoid-everlasting-service.java:20:9:20:38 | startService(...) | startService() is called here without a corresponding stopService() or stopSelf() in the same method. This may keep the service alive indefinitely and cause uncontrolled energy leakage. | +| avoid-everlasting-service.java:21:9:21:38 | startService(...) | startService() is called here without a corresponding stopService() or stopSelf() in the same method. This may keep the service alive indefinitely and cause uncontrolled energy leakage. | +| avoid-everlasting-service.java:26:9:26:38 | startService(...) | startService() is called here without a corresponding stopService() or stopSelf() in the same method. This may keep the service alive indefinitely and cause uncontrolled energy leakage. | diff --git a/codeql-custom-queries-java/android/avoid-everlasting-service.java b/codeql-custom-queries-java/android/avoid-everlasting-service.java new file mode 100644 index 0000000..a8fc20b --- /dev/null +++ b/codeql-custom-queries-java/android/avoid-everlasting-service.java @@ -0,0 +1,61 @@ +class Context { + Object startService(Object service) { return null; } + boolean stopService(Object service) { return false; } +} + +class Service extends Context {} + +class Intent {} + +// 🚫 Noncompliant - startService() without stopService() or stopSelf() +class NoncompliantEverlastingService { + + void leakingLaunch() { + Context ctx = new Context(); + ctx.startService(new Intent()); // $ Alert + } + + void leakingLaunchMultiple() { + Context ctx = new Context(); + ctx.startService(new Intent()); // $ Alert + ctx.startService(new Intent()); // $ Alert + } + + void leakingNoStopInSameMethod() { + Context ctx = new Context(); + ctx.startService(new Intent()); // $ Alert + } + + void stopElsewhere() { + Context ctx = new Context(); + ctx.stopService(new Intent()); + } +} + +// ✅ Compliant - startService() with stopService() in same method +class CompliantStopService { + + void safeWithStopService() { + Context ctx = new Context(); + ctx.startService(new Intent()); + ctx.stopService(new Intent()); + } + + void safeMultiple() { + Context ctx = new Context(); + ctx.startService(new Intent()); + ctx.startService(new Intent()); + ctx.stopService(new Intent()); + } +} + +// ✅ Compliant - startService() with stopSelf() in same method +class CompliantStopSelf extends Service { + + void safeWithStopSelf() { + startService(new Intent()); + stopSelf(); + } + + void stopSelf() {} +} \ No newline at end of file diff --git a/codeql-custom-queries-java/android/avoid-everlasting-service.ql b/codeql-custom-queries-java/android/avoid-everlasting-service.ql new file mode 100644 index 0000000..89c1381 --- /dev/null +++ b/codeql-custom-queries-java/android/avoid-everlasting-service.ql @@ -0,0 +1,28 @@ +/** + * @name Avoid Everlasting Service + * @description If someone calls Context#startService() then the service will continue + * running until Context#stopService() or Service#stopSelf() is called. + * Failing to call any of these methods can lead to uncontrolled energy leakage. + * @kind problem + * @problem.severity warning + * @precision medium + * @id java/android/avoid-everlasting-service + * @tags android + * @tags java + */ + +import java + +from MethodCall startCall +where + startCall.getMethod().getName() = "startService" and + not exists(MethodCall stopCall | + stopCall.getEnclosingCallable() = startCall.getEnclosingCallable() and + ( + stopCall.getMethod().getName() = "stopService" + or + stopCall.getMethod().getName() = "stopSelf" + ) + ) +select startCall, + "startService() is called here without a corresponding stopService() or stopSelf() in the same method. This may keep the service alive indefinitely and cause uncontrolled energy leakage." \ No newline at end of file diff --git a/codeql-custom-queries-java/android/avoid-internet-in-loop.expected b/codeql-custom-queries-java/android/avoid-internet-in-loop.expected new file mode 100644 index 0000000..82a92be --- /dev/null +++ b/codeql-custom-queries-java/android/avoid-internet-in-loop.expected @@ -0,0 +1,5 @@ +| avoid-internet-in-loop.java:18:13:18:32 | openConnection(...) | URL#openConnection() is called inside a loop. Opening HTTP connections in a loop is battery-inefficient. Consider using push notifications instead of polling. | +| avoid-internet-in-loop.java:26:13:26:32 | openConnection(...) | URL#openConnection() is called inside a loop. Opening HTTP connections in a loop is battery-inefficient. Consider using push notifications instead of polling. | +| avoid-internet-in-loop.java:34:13:34:32 | openConnection(...) | URL#openConnection() is called inside a loop. Opening HTTP connections in a loop is battery-inefficient. Consider using push notifications instead of polling. | +| avoid-internet-in-loop.java:43:13:43:32 | openConnection(...) | URL#openConnection() is called inside a loop. Opening HTTP connections in a loop is battery-inefficient. Consider using push notifications instead of polling. | +| avoid-internet-in-loop.java:51:17:51:36 | openConnection(...) | URL#openConnection() is called inside a loop. Opening HTTP connections in a loop is battery-inefficient. Consider using push notifications instead of polling. | diff --git a/codeql-custom-queries-java/android/avoid-internet-in-loop.java b/codeql-custom-queries-java/android/avoid-internet-in-loop.java new file mode 100644 index 0000000..b2053f8 --- /dev/null +++ b/codeql-custom-queries-java/android/avoid-internet-in-loop.java @@ -0,0 +1,81 @@ +class URL { + Object openConnection() { return null; } +} + +class HttpURLConnection { + void connect() {} + void disconnect() {} + int getResponseCode() { return 200; } +} + +// 🚫 Noncompliant - openConnection() inside loops +class NoncompliantInternetInLoop { + + void fetchInWhileLoop() throws Exception { + int i = 0; + while (i < 10) { + URL url = new URL(); + url.openConnection(); // $ Alert + i++; + } + } + + void fetchInForLoop() throws Exception { + for (int i = 0; i < 10; i++) { + URL url = new URL(); + url.openConnection(); // $ Alert + } + } + + void fetchInDoWhileLoop() throws Exception { + int i = 0; + do { + URL url = new URL(); + url.openConnection(); // $ Alert + i++; + } while (i < 10); + } + + void fetchInForEachLoop() throws Exception { + String[] endpoints = {"http://a.com", "http://b.com"}; + for (String endpoint : endpoints) { + URL url = new URL(); + url.openConnection(); // $ Alert + } + } + + void fetchInNestedLoop() throws Exception { + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 5; j++) { + URL url = new URL(); + url.openConnection(); // $ Alert + } + } + } +} + +// ✅ Compliant - openConnection() outside loops +class CompliantInternetUsage { + + void fetchOnce() throws Exception { + URL url = new URL(); + url.openConnection(); + } + + void fetchBeforeLoop() throws Exception { + URL url = new URL(); + url.openConnection(); + for (int i = 0; i < 10; i++) { + // traitement sans nouvelle connexion + int x = i * 2; + } + } + + void processItems() throws Exception { + String[] items = {"a", "b", "c"}; + for (String item : items) { + // traitement sans ouvrir de connexion + String result = item.toUpperCase(); + } + } +} \ No newline at end of file diff --git a/codeql-custom-queries-java/android/avoid-internet-in-loop.ql b/codeql-custom-queries-java/android/avoid-internet-in-loop.ql new file mode 100644 index 0000000..76acc6e --- /dev/null +++ b/codeql-custom-queries-java/android/avoid-internet-in-loop.ql @@ -0,0 +1,23 @@ +/** + * @name Internet In The Loop + * @description Opening and closing internet connections continuously is extremely battery-inefficient. + * Obtaining a new HttpURLConnection by calling URL#openConnection() within a loop + * control structure (while, for, do-while, for-each) is a bad practice that leads + * to uncontrolled energy leakage and prevents the use of push notifications. + * @kind problem + * @problem.severity warning + * @precision high + * @id java/android/internet-in-loop + * @link https://green-code-initiative.org/rules#id:GCI502 + * @tags android + * @tags java + */ + +import java + +from MethodCall openCall, LoopStmt loop +where + openCall.getMethod().getName() = "openConnection" and + loop.getBody().getAChild*() = openCall.getEnclosingStmt() +select openCall, + "URL#openConnection() is called inside a loop. Opening HTTP connections in a loop is battery-inefficient. Consider using push notifications instead of polling." \ No newline at end of file diff --git a/codeql-custom-queries-java/android/avoid-ligth-ui.expected b/codeql-custom-queries-java/android/avoid-ligth-ui.expected new file mode 100644 index 0000000..c0c8fc8 --- /dev/null +++ b/codeql-custom-queries-java/android/avoid-ligth-ui.expected @@ -0,0 +1,6 @@ +| avoid-day-night-mode.xml:5:5:5:65 | parent=Theme.AppCompat.Light | Avoid using light themes ('Theme.AppCompat.Light'). Light themes consume more energy on (AM)OLED screens. Prefer a dark theme instead. | +| avoid-day-night-mode.xml:17:5:17:83 | parent=Theme.AppCompat.Light.DarkActionBar | Avoid using light themes ('Theme.AppCompat.Light.DarkActionBar'). Light themes consume more energy on (AM)OLED screens. Prefer a dark theme instead. | +| avoid-ligth-ui.xml:5:5:5:65 | parent=Theme.AppCompat.Light | Avoid using light themes ('Theme.AppCompat.Light'). Light themes consume more energy on (AM)OLED screens. Prefer a dark theme instead. | +| avoid-ligth-ui.xml:9:5:9:82 | parent=Theme.MaterialComponents.Light | Avoid using light themes ('Theme.MaterialComponents.Light'). Light themes consume more energy on (AM)OLED screens. Prefer a dark theme instead. | +| avoid-ligth-ui.xml:13:5:13:83 | parent=Theme.AppCompat.Light.DarkActionBar | Avoid using light themes ('Theme.AppCompat.Light.DarkActionBar'). Light themes consume more energy on (AM)OLED screens. Prefer a dark theme instead. | +| avoid-ligth-ui.xml:17:5:17:100 | parent=Theme.MaterialComponents.Light.DarkActionBar | Avoid using light themes ('Theme.MaterialComponents.Light.DarkActionBar'). Light themes consume more energy on (AM)OLED screens. Prefer a dark theme instead. | diff --git a/codeql-custom-queries-java/android/avoid-ligth-ui.ql b/codeql-custom-queries-java/android/avoid-ligth-ui.ql new file mode 100644 index 0000000..0a2697a --- /dev/null +++ b/codeql-custom-queries-java/android/avoid-ligth-ui.ql @@ -0,0 +1,24 @@ +/** + * @name Ligth UI + * @description Using a light theme (Theme.*.Light) increases energy consumption on (AM)OLED screens. + * By default Android sets a dark style — switching to a light theme should be avoided. + * Custom resources with bright colors or high-luminance bitmaps should also be avoided. + * @kind problem + * @problem.severity warning + * @precision high + * @id java/android/avoid-ligth-ui + * @tags android + * @tags java + * + * @note Test contains those for the day nigth theme detection too + */ + +import semmle.code.xml.XML + +from XmlAttribute attr +where + attr.getName() = "parent" and + attr.getValue().matches("%Light%") and + attr.getElement().getName() = "style" +select attr, + "Avoid using light themes ('" + attr.getValue() + "'). Light themes consume more energy on (AM)OLED screens. Prefer a dark theme instead." \ No newline at end of file diff --git a/codeql-custom-queries-java/android/avoid-ligth-ui.xml b/codeql-custom-queries-java/android/avoid-ligth-ui.xml new file mode 100644 index 0000000..78cfab6 --- /dev/null +++ b/codeql-custom-queries-java/android/avoid-ligth-ui.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/codeql-custom-queries-java/android/avoid-uncached-data-reception.expected b/codeql-custom-queries-java/android/avoid-uncached-data-reception.expected new file mode 100644 index 0000000..dce98a9 --- /dev/null +++ b/codeql-custom-queries-java/android/avoid-uncached-data-reception.expected @@ -0,0 +1,2 @@ +| avoid-uncached-data-reception.java:14:13:14:39 | getInputStream(...) | Avoid not caching HTTP responses. Use UncachedDataCache.install() in the same method to cache responses and save energy. | +| avoid-uncached-data-reception.java:47:32:47:59 | getResponseCode(...) | Avoid not caching HTTP responses. Use UncachedDataCache.install() in the same method to cache responses and save energy. | diff --git a/codeql-custom-queries-java/android/avoid-uncached-data-reception.java b/codeql-custom-queries-java/android/avoid-uncached-data-reception.java new file mode 100644 index 0000000..c3d9172 --- /dev/null +++ b/codeql-custom-queries-java/android/avoid-uncached-data-reception.java @@ -0,0 +1,72 @@ +// Stub minimal pour HttpResponseCache +class UncachedDataCache { + static Object install(Object directory, long maxSize) { return null; } +} + +// 🚫 Noncompliant - Not using HttpResponseCache +class NoncompliantUncachedRequest { + void fetchData() { + try { + java.net.URL url = new java.net.URL("http://example.com"); + java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection(); + + // $ Alert: No cache installed + connection.getInputStream(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} + +// ✅ Compliant - Using HttpResponseCache +class CompliantCachedRequest { + void fetchCachedData() { + try { + // OK: Cache installed at startup + java.io.File cacheDir = new java.io.File("cache"); + UncachedDataCache.install(cacheDir, 1024 * 1024); // 1MB + + java.net.URL url = new java.net.URL("http://example.com"); + java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection(); + + connection.getInputStream(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} + +// 🚫 Noncompliant - Not using HttpResponseCache +class NoncompliantUncachedMethodRequest { + void fetchDataFromServer() { + try { + java.net.URL url = new java.net.URL("http://example.com"); + java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection(); + + // $ Alert: No cache installed + int responseCode = connection.getResponseCode(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} + +// ✅ Compliant - Using HttpResponseCache in a static block +class CompliantStaticCachedRequest { + static { + // OK: Cache installed in static block + java.io.File cacheDir = new java.io.File("cache"); + UncachedDataCache.install(cacheDir, 1024 * 1024); // 1MB + } + + void fetchCachedData() { + try { + java.net.URL url = new java.net.URL("http://example.com"); + java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection(); + + connection.getInputStream(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/codeql-custom-queries-java/android/avoid-uncached-data-reception.ql b/codeql-custom-queries-java/android/avoid-uncached-data-reception.ql new file mode 100644 index 0000000..597efa5 --- /dev/null +++ b/codeql-custom-queries-java/android/avoid-uncached-data-reception.ql @@ -0,0 +1,30 @@ +/** + * @name Uncached Data Reception + * @description Detects if an HTTP call (getInputStream or getResponseCode) is not directly associated with UncachedDataCache.install() in the same method. + * @kind problem + * @problem.severity warning + * @id java/android/avoid-uncached-data-reception + * @tags android + * @tags java + */ + +import java + +from MethodCall httpCall +where + ( + httpCall.getMethod().hasName("getInputStream") or + httpCall.getMethod().hasName("getResponseCode") + ) and + not exists(MethodCall installCall | + installCall.getMethod().hasName("install") and + ( + installCall.getEnclosingCallable() = httpCall.getEnclosingCallable() + or + installCall.getEnclosingCallable() instanceof StaticInitializer and + installCall.getEnclosingCallable().getDeclaringType() = + httpCall.getEnclosingCallable().getDeclaringType() + ) + ) +select httpCall, + "Avoid not caching HTTP responses. Use UncachedDataCache.install() in the same method to cache responses and save energy." \ No newline at end of file diff --git a/codeql-custom-queries-java/android/avoid-uncompressed-data-transmission.expected b/codeql-custom-queries-java/android/avoid-uncompressed-data-transmission.expected new file mode 100644 index 0000000..9bc79dc --- /dev/null +++ b/codeql-custom-queries-java/android/avoid-uncompressed-data-transmission.expected @@ -0,0 +1,3 @@ +| avoid-uncompressed-data-transmission.java:11:39:11:66 | getOutputStream(...) | Avoid using raw OutputStream for HTTP requests. Use GZIPOutputStream to compress data and reduce energy consumption. | +| avoid-uncompressed-data-transmission.java:51:81:51:108 | getOutputStream(...) | Avoid using raw OutputStream for HTTP requests. Use GZIPOutputStream to compress data and reduce energy consumption. | +| avoid-uncompressed-data-transmission.java:93:39:93:66 | getOutputStream(...) | Avoid using raw OutputStream for HTTP requests. Use GZIPOutputStream to compress data and reduce energy consumption. | diff --git a/codeql-custom-queries-java/android/avoid-uncompressed-data-transmission.java b/codeql-custom-queries-java/android/avoid-uncompressed-data-transmission.java new file mode 100644 index 0000000..94bee33 --- /dev/null +++ b/codeql-custom-queries-java/android/avoid-uncompressed-data-transmission.java @@ -0,0 +1,125 @@ +// 🚫 Noncompliant - Using raw OutputStream for HTTP request +class NoncompliantHttpRequest { + + void sendData() { + try { + java.net.URL url = new java.net.URL("http://example.com"); + java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection(); + connection.setDoOutput(true); + + // $ Alert: Using raw OutputStream + java.io.OutputStream os = connection.getOutputStream(); + + os.write("Hello, world!".getBytes()); + os.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} + +// ✅ Compliant - Using GZIPOutputStream for HTTP request +class CompliantHttpRequest { + + void sendCompressedData() { + try { + java.net.URL url = new java.net.URL("http://example.com"); + java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection(); + connection.setDoOutput(true); + + // OK: Using GZIPOutputStream + java.util.zip.GZIPOutputStream gzipOs = new java.util.zip.GZIPOutputStream(connection.getOutputStream()); + + gzipOs.write("Hello, world!".getBytes()); + gzipOs.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} + +// 🚫 Noncompliant - Using raw OutputStream with BufferedOutputStream +class NoncompliantBufferedRequest { + + void sendBufferedData() { + try { + java.net.URL url = new java.net.URL("http://example.com"); + java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection(); + connection.setDoOutput(true); + + // $ Alert: Using raw OutputStream (even with BufferedOutputStream) + java.io.BufferedOutputStream bos = new java.io.BufferedOutputStream(connection.getOutputStream()); + + bos.write("Hello, world!".getBytes()); + bos.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} + +// ✅ Compliant - Using GZIPOutputStream with BufferedOutputStream +class CompliantBufferedRequest { + + void sendCompressedBufferedData() { + try { + java.net.URL url = new java.net.URL("http://example.com"); + java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection(); + connection.setDoOutput(true); + + // OK: Using GZIPOutputStream with BufferedOutputStream + java.io.BufferedOutputStream bos = new java.io.BufferedOutputStream( + new java.util.zip.GZIPOutputStream(connection.getOutputStream()) + ); + + bos.write("Hello, world!".getBytes()); + bos.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} + +// 🚫 Noncompliant - Using raw OutputStream in a loop +class NoncompliantLoopRequest { + + void sendMultipleData() { + try { + java.net.URL url = new java.net.URL("http://example.com"); + java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection(); + connection.setDoOutput(true); + + // $ Alert: Using raw OutputStream in a loop + java.io.OutputStream os = connection.getOutputStream(); + + for (int i = 0; i < 10; i++) { + os.write(("Data " + i).getBytes()); + } + os.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} + +// ✅ Compliant - Using GZIPOutputStream in a loop +class CompliantLoopRequest { + + void sendCompressedMultipleData() { + try { + java.net.URL url = new java.net.URL("http://example.com"); + java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection(); + connection.setDoOutput(true); + + // OK: Using GZIPOutputStream in a loop + java.util.zip.GZIPOutputStream gzipOs = new java.util.zip.GZIPOutputStream(connection.getOutputStream()); + + for (int i = 0; i < 10; i++) { + gzipOs.write(("Data " + i).getBytes()); + } + gzipOs.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/codeql-custom-queries-java/android/avoid-uncompressed-data-transmission.ql b/codeql-custom-queries-java/android/avoid-uncompressed-data-transmission.ql new file mode 100644 index 0000000..8d2dedb --- /dev/null +++ b/codeql-custom-queries-java/android/avoid-uncompressed-data-transmission.ql @@ -0,0 +1,33 @@ +/** + * @name Uncompressed Data Transmission + * @description Transmitting a file over a network infrastructure without compressing it consumes more energy than with compression. Using GZIPOutputStream for HTTP requests reduces energy consumption by compressing data at least by 10% before transmission. + * @kind problem + * @problem.severity warning + * @precision high + * @id java/android/uncompressed-data-transmission + * @link https://green-code-initiative.org/rules#id:GCI504 + * @tags android + * @tags java + * @tags energy + */ + +import java + +from MethodCall mc +where + mc.getMethod().getName() = "getOutputStream" and + not exists( + // Vérifie si le OutputStream est utilisé dans un appel de constructeur GZIPOutputStream + ConstructorCall gzipCtor | + gzipCtor.getConstructor().getDeclaringType().getName() = "GZIPOutputStream" and + gzipCtor.getAnArgument() = mc + ) and + not exists( + // Vérifie si le OutputStream est utilisé dans une méthode write() + MethodCall writeCall | + writeCall.getMethod().getName() = "write" and + writeCall.getAnArgument() instanceof Literal and + exists(Expr parent | parent = mc.getParent() and parent = writeCall) + ) +select mc, + "Avoid using raw OutputStream for HTTP requests. Use GZIPOutputStream to compress data and reduce energy consumption." \ No newline at end of file diff --git a/codeql-custom-queries-java/android/avoid-wifi-multicast-lock.expected b/codeql-custom-queries-java/android/avoid-wifi-multicast-lock.expected new file mode 100644 index 0000000..01a20b2 --- /dev/null +++ b/codeql-custom-queries-java/android/avoid-wifi-multicast-lock.expected @@ -0,0 +1,4 @@ +| avoid-wifi-multicast-lock.java:14:9:14:22 | acquire(...) | WifiManager.MulticastLock.acquire() is called here without a corresponding release() in the same method. This can cause significant battery drain. | +| avoid-wifi-multicast-lock.java:27:9:27:22 | acquire(...) | WifiManager.MulticastLock.acquire() is called here without a corresponding release() in the same method. This can cause significant battery drain. | +| avoid-wifi-multicast-lock.java:37:13:37:26 | acquire(...) | WifiManager.MulticastLock.acquire() is called here without a corresponding release() in the same method. This can cause significant battery drain. | +| avoid-wifi-multicast-lock.java:50:17:50:30 | acquire(...) | WifiManager.MulticastLock.acquire() is called here without a corresponding release() in the same method. This can cause significant battery drain. | diff --git a/codeql-custom-queries-java/android/avoid-wifi-multicast-lock.java b/codeql-custom-queries-java/android/avoid-wifi-multicast-lock.java new file mode 100644 index 0000000..6723ecf --- /dev/null +++ b/codeql-custom-queries-java/android/avoid-wifi-multicast-lock.java @@ -0,0 +1,107 @@ +// Minimal stubs for Android SDK classes not available in javac +class WifiManager { + class MulticastLock { + void acquire() {} + void release() {} + } +} + +// 🚫 Noncompliant - acquire without release +class Noncompliant1WifiMulticast { + void method1() { + WifiManager manager = new WifiManager(); + WifiManager.MulticastLock lock = manager.new MulticastLock(); + lock.acquire(); // $ Alert + } +} + +// 🚫 Noncompliant - acquire in a separate method without release +class Noncompliant2WifiMulticast { + void method1() { + acquireLock(); + } + + void acquireLock() { + WifiManager manager = new WifiManager(); + WifiManager.MulticastLock lock = manager.new MulticastLock(); + lock.acquire(); // $ Alert + } +} + +// 🚫 Noncompliant - acquire in a loop without release +class Noncompliant3WifiMulticast { + void method1() { + WifiManager manager = new WifiManager(); + WifiManager.MulticastLock lock = manager.new MulticastLock(); + for (int i = 0; i < 10; i++) { + lock.acquire(); // $ Alert + } + } +} + +// 🚫 Noncompliant - acquire in an inner class without release +class Noncompliant4WifiMulticast { + void method1() { + WifiManager manager = new WifiManager(); + WifiManager.MulticastLock lock = manager.new MulticastLock(); + Runnable runnable = new Runnable() { + @Override + public void run() { + lock.acquire(); // $ Alert + } + }; + runnable.run(); + } +} + +// ✅ Compliant - acquire with release +class Compliant1WifiMulticast { + void method1() { + WifiManager manager = new WifiManager(); + WifiManager.MulticastLock lock = manager.new MulticastLock(); + lock.acquire(); // OK + lock.release(); // OK + } +} + +// ✅ Compliant - acquire and release in separate methods +class Compliant2WifiMulticast { + void method1() { + acquireAndRelease(); + } + + void acquireAndRelease() { + WifiManager manager = new WifiManager(); + WifiManager.MulticastLock lock = manager.new MulticastLock(); + lock.acquire(); // OK + lock.release(); // OK + } +} + +// ✅ Compliant - acquire and release in a loop +class Compliant3WifiMulticast { + void method1() { + WifiManager manager = new WifiManager(); + WifiManager.MulticastLock lock = manager.new MulticastLock(); + for (int i = 0; i < 10; i++) { + lock.acquire(); // OK + lock.release(); // OK + } + } +} + +// ✅ Compliant - acquire and release in an inner class +class Compliant4WifiMulticast { + void method1() { + WifiManager manager = new WifiManager(); + WifiManager.MulticastLock lock = manager.new MulticastLock(); + Runnable runnable = new Runnable() { + @Override + public void run() { + lock.acquire(); // OK + lock.release(); // OK + } + }; + runnable.run(); + } +} diff --git a/codeql-custom-queries-java/android/avoid-wifi-multicast-lock.ql b/codeql-custom-queries-java/android/avoid-wifi-multicast-lock.ql new file mode 100644 index 0000000..91c5a01 --- /dev/null +++ b/codeql-custom-queries-java/android/avoid-wifi-multicast-lock.ql @@ -0,0 +1,25 @@ +/** + * @name Unreleased Wifi Multicast Lock + * @description Acquiring a WifiManager.MulticastLock with `acquire()` allows the device to receive multicast packets, + * which can cause significant battery drain. The lock must be released with `release()` when no longer needed. + * Failing to do so can lead to uncontrolled energy consumption. + * @kind problem + * @problem.severity warning + * @precision high + * @id java/android/avoid-wifi-multicast-lock + * @link https://green-code-initiative.org/rules#id:GCI503 + * @tags android + * @tags java + */ + +import java + +from MethodCall acquireCall +where + acquireCall.getMethod().getName() = "acquire" and + not exists(MethodCall releaseCall | + releaseCall.getEnclosingCallable() = acquireCall.getEnclosingCallable() and + releaseCall.getMethod().getName() = "release" + ) +select acquireCall, + "WifiManager.MulticastLock.acquire() is called here without a corresponding release() in the same method. This can cause significant battery drain." diff --git a/codeql-custom-queries-java/android/prefer-bluetooth-low-energy.expected b/codeql-custom-queries-java/android/prefer-bluetooth-low-energy.expected index c96a7dc..8911795 100644 --- a/codeql-custom-queries-java/android/prefer-bluetooth-low-energy.expected +++ b/codeql-custom-queries-java/android/prefer-bluetooth-low-energy.expected @@ -1,11 +1,11 @@ -| prefer-bluetooth-low-energy.java:25:28:25:49 | device | Avoid classic Bluetooth ('BluetoothDevice'). Use Bluetooth Low Energy APIs from android.bluetooth.le instead to significantly reduce power consumption. | -| prefer-bluetooth-low-energy.java:31:22:31:28 | adapter | Avoid classic Bluetooth ('BluetoothAdapter'). Use Bluetooth Low Energy APIs from android.bluetooth.le instead to significantly reduce power consumption. | -| prefer-bluetooth-low-energy.java:34:21:34:26 | device | Avoid classic Bluetooth ('BluetoothDevice'). Use Bluetooth Low Energy APIs from android.bluetooth.le instead to significantly reduce power consumption. | -| prefer-bluetooth-low-energy.java:37:21:37:26 | socket | Avoid classic Bluetooth ('BluetoothSocket'). Use Bluetooth Low Energy APIs from android.bluetooth.le instead to significantly reduce power consumption. | -| prefer-bluetooth-low-energy.java:40:27:40:38 | serverSocket | Avoid classic Bluetooth ('BluetoothServerSocket'). Use Bluetooth Low Energy APIs from android.bluetooth.le instead to significantly reduce power consumption. | -| prefer-bluetooth-low-energy.java:43:22:43:28 | profile | Avoid classic Bluetooth ('BluetoothProfile'). Use Bluetooth Low Energy APIs from android.bluetooth.le instead to significantly reduce power consumption. | -| prefer-bluetooth-low-energy.java:47:9:47:77 | BluetoothAdapter localAdapter | Avoid classic Bluetooth ('BluetoothAdapter'). Use Bluetooth Low Energy APIs from android.bluetooth.le instead to significantly reduce power consumption. | -| prefer-bluetooth-low-energy.java:53:9:53:66 | BluetoothAdapter a | Avoid classic Bluetooth ('BluetoothAdapter'). Use Bluetooth Low Energy APIs from android.bluetooth.le instead to significantly reduce power consumption. | -| prefer-bluetooth-low-energy.java:54:9:54:66 | BluetoothDevice remoteDevice | Avoid classic Bluetooth ('BluetoothDevice'). Use Bluetooth Low Energy APIs from android.bluetooth.le instead to significantly reduce power consumption. | -| prefer-bluetooth-low-energy.java:59:9:59:33 | BluetoothSocket s | Avoid classic Bluetooth ('BluetoothSocket'). Use Bluetooth Low Energy APIs from android.bluetooth.le instead to significantly reduce power consumption. | -| prefer-bluetooth-low-energy.java:65:9:65:40 | BluetoothServerSocket ss | Avoid classic Bluetooth ('BluetoothServerSocket'). Use Bluetooth Low Energy APIs from android.bluetooth.le instead to significantly reduce power consumption. | +| prefer-bluetooth-low-energy.java:23:28:23:49 | device | Avoid classic Bluetooth ('BluetoothDevice'). Use Bluetooth Low Energy APIs from android.bluetooth.le instead to significantly reduce power consumption. | +| prefer-bluetooth-low-energy.java:29:22:29:28 | adapter | Avoid classic Bluetooth ('BluetoothAdapter'). Use Bluetooth Low Energy APIs from android.bluetooth.le instead to significantly reduce power consumption. | +| prefer-bluetooth-low-energy.java:32:21:32:26 | device | Avoid classic Bluetooth ('BluetoothDevice'). Use Bluetooth Low Energy APIs from android.bluetooth.le instead to significantly reduce power consumption. | +| prefer-bluetooth-low-energy.java:35:21:35:26 | socket | Avoid classic Bluetooth ('BluetoothSocket'). Use Bluetooth Low Energy APIs from android.bluetooth.le instead to significantly reduce power consumption. | +| prefer-bluetooth-low-energy.java:38:27:38:38 | serverSocket | Avoid classic Bluetooth ('BluetoothServerSocket'). Use Bluetooth Low Energy APIs from android.bluetooth.le instead to significantly reduce power consumption. | +| prefer-bluetooth-low-energy.java:41:22:41:28 | profile | Avoid classic Bluetooth ('BluetoothProfile'). Use Bluetooth Low Energy APIs from android.bluetooth.le instead to significantly reduce power consumption. | +| prefer-bluetooth-low-energy.java:45:9:45:77 | BluetoothAdapter localAdapter | Avoid classic Bluetooth ('BluetoothAdapter'). Use Bluetooth Low Energy APIs from android.bluetooth.le instead to significantly reduce power consumption. | +| prefer-bluetooth-low-energy.java:51:9:51:66 | BluetoothAdapter a | Avoid classic Bluetooth ('BluetoothAdapter'). Use Bluetooth Low Energy APIs from android.bluetooth.le instead to significantly reduce power consumption. | +| prefer-bluetooth-low-energy.java:52:9:52:66 | BluetoothDevice remoteDevice | Avoid classic Bluetooth ('BluetoothDevice'). Use Bluetooth Low Energy APIs from android.bluetooth.le instead to significantly reduce power consumption. | +| prefer-bluetooth-low-energy.java:57:9:57:33 | BluetoothSocket s | Avoid classic Bluetooth ('BluetoothSocket'). Use Bluetooth Low Energy APIs from android.bluetooth.le instead to significantly reduce power consumption. | +| prefer-bluetooth-low-energy.java:63:9:63:40 | BluetoothServerSocket ss | Avoid classic Bluetooth ('BluetoothServerSocket'). Use Bluetooth Low Energy APIs from android.bluetooth.le instead to significantly reduce power consumption. | diff --git a/codeql-custom-queries-java/android/prefer-bluetooth-low-energy.java b/codeql-custom-queries-java/android/prefer-bluetooth-low-energy.java index 6b61fcf..9dacc6f 100644 --- a/codeql-custom-queries-java/android/prefer-bluetooth-low-energy.java +++ b/codeql-custom-queries-java/android/prefer-bluetooth-low-energy.java @@ -1,5 +1,3 @@ -package android.bluetooth; - class BluetoothAdapter { public static BluetoothAdapter getDefaultAdapter() { return null; } public BluetoothDevice getRemoteDevice(String address) { return null; }