CVE-2026-106367
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifui/android/java/src/org/chromium/ui/dragdrop/DropDataProviderImpl.java |
modified | |
switchui/android/java/src/org/chromium/ui/dragdrop/DropDataProviderImpl.java |
modified | |
synchronizedui/android/java/src/org/chromium/ui/dragdrop/DropDataProviderImpl.java |
modified | |
ifui/android/java/src/org/chromium/ui/dragdrop/DropDataProviderUtils.java |
modified |
Files Changed
ui/android/java/src/org/chromium/ui/dragdrop/DropDataProviderImpl.javaui/android/java/src/org/chromium/ui/dragdrop/DropDataProviderUtils.javaui/android/junit/src/org/chromium/ui/dragdrop/DropDataProviderImplTest.java
Patch
From 2fd647a2dd32b17669a66cf6686b0fd06dc3c096 Mon Sep 17 00:00:00 2001
From: Shu Yang <shuyng@google.com>
Date: Fri, 11 Sep 2026 18:24:02 -0700
Subject: [PATCH] Validate caller UID and input parameters in DropDataProviderImpl
DropDataProviderImpl.call() handles internal drag-and-drop cache
management. This CL adds a check to ensure the calling UID matches the
current process UID before executing provider commands. Parameter
handling is also updated to read raw byte arrays directly using
getByteArray rather than getSerializable, and explicit null checks
replace runtime assertions.
In addition, matchMimeType() now validates that both the MIME type and
the filter contain a delimiter prior to substring indexing, preventing
boundary limit exceptions on ill-formed filter strings.
Bug: 520533412
Change-Id: Ia8b714b88530db36002a8ebf663624c802ac13cf
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8382441
Commit-Queue: Shu Yang <shuyng@google.com>
Reviewed-by: Sinan Sahin <sinansahin@google.com>
Cr-Commit-Position: refs/heads/main@{#1696769}
---
diff --git a/ui/android/java/src/org/chromium/ui/dragdrop/DropDataProviderImpl.java b/ui/android/java/src/org/chromium/ui/dragdrop/DropDataProviderImpl.java
index cc9de8d..c746b7e 100644
--- a/ui/android/java/src/org/chromium/ui/dragdrop/DropDataProviderImpl.java
+++ b/ui/android/java/src/org/chromium/ui/dragdrop/DropDataProviderImpl.java
@@ -12,10 +12,12 @@
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.SystemClock;
import android.provider.OpenableColumns;
import android.webkit.MimeTypeMap;
@@ -50,6 +52,7 @@
public static final String BYTES_PARAM = "bytes";
public static final String IMAGE_CONTENT_EXTENSION_PARAM = "imageContentExtension";
public static final String IMAGE_FILE_PARAM = "imageFilename";
+ public static final String URI_PARAM = "uri";
public static final int DEFAULT_CLEAR_CACHED_DATA_INTERVAL_MS = 60_000;
/**
@@ -251,9 +254,12 @@
}
int idx1 = mimeType.indexOf('/');
+ int idx2 = mimeTypeFilter.indexOf('/');
+ if (idx1 == -1 || idx2 == -1) {
+ return false;
+ }
String type = mimeType.substring(0, idx1);
String subtype = mimeType.substring(idx1 + 1);
- int idx2 = mimeTypeFilter.indexOf('/');
String typeFilter = mimeTypeFilter.substring(0, idx2);
String subtypeFilter = mimeTypeFilter.substring(idx2 + 1);
if (!typeFilter.equals("*") && !typeFilter.equals(type)) {
@@ -357,30 +363,38 @@
/**
* @see ContentProvider#call(String, String, Bundle)
+ * <p>Note: Although ContentProvider#call specifies `method` as @NonNull, it is
+ * marked @Nullable here to defensively handle crafted IPC calls. A malicious app bypassing
+ * the ContentResolver could send a null method via Binder, which would otherwise cause a
+ * NullPointerException when switching on the method name.
*/
- public @Nullable Bundle call(String method, @Nullable String arg, @Nullable Bundle extras) {
+ public @Nullable Bundle call(
+ @Nullable String method, @Nullable String arg, @Nullable Bundle extras) {
+ if (Binder.getCallingUid() != Process.myUid()) {
+ throw new SecurityException("Calling UID does not match process UID.");
+ }
+ if (method == null || extras == null) {
+ return null;
+ }
switch (method) {
case CACHE_METHOD_NAME:
- assumeNonNull(extras);
- byte[] imageBytes = (byte[]) extras.getSerializable(BYTES_PARAM);
+ byte[] imageBytes = extras.getByteArray(BYTES_PARAM);
String encodingFormat = extras.getString(IMAGE_CONTENT_EXTENSION_PARAM);
String filename = extras.getString(IMAGE_FILE_PARAM);
- assert imageBytes != null;
- assert encodingFormat != null;
- assert filename != null;
+ if (imageBytes == null || encodingFormat == null || filename == null) {
+ return null;
+ }
Uri uri = cache(imageBytes, encodingFormat, filename);
Bundle bundleToReturn = new Bundle();
- bundleToReturn.putParcelable("uri", uri);
+ bundleToReturn.putParcelable(URI_PARAM, uri);
return bundleToReturn;
case SET_INTERVAL_METHOD_NAME:
- assumeNonNull(extras);
setClearCachedDataIntervalMs(
extras.getInt(
CLEAR_CACHE_PARAM,
DropDataProviderImpl.DEFAULT_CLEAR_CACHED_DATA_INTERVAL_MS));
break;
case ON_DRAG_END_METHOD_NAME:
- assumeNonNull(extras);
onDragEnd(extras.getBoolean(IMAGE_USAGE_PARAM));
break;
}
@@ -388,6 +402,12 @@
return null;
}
+ int getClearCachedDataIntervalMsForTesting() {
+ synchronized (LOCK) {
+ return mClearCachedDataIntervalMs;
+ }
+ }
+
byte @Nullable [] getImageBytesForTesting() {
synchronized (LOCK) {
return mImageBytes;
diff --git a/ui/android/java/src/org/chromium/ui/dragdrop/DropDataProviderUtils.java b/ui/android/java/src/org/chromium/ui/dragdrop/DropDataProviderUtils.java
index 8cb02695e..690dcb2 100644
--- a/ui/android/java/src/org/chromium/ui/dragdrop/DropDataProviderUtils.java
+++ b/ui/android/java/src/org/chromium/ui/dragdrop/DropDataProviderUtils.java
@@ -4,10 +4,10 @@
package org.chromium.ui.dragdrop;
-import static org.chromium.build.NullUtil.assumeNonNull;
import static org.chromium.ui.dragdrop.DropDataProviderImpl.CACHE_METHOD_NAME;
import static org.chromium.ui.dragdrop.DropDataProviderImpl.ON_DRAG_END_METHOD_NAME;
import static org.chromium.ui.dragdrop.DropDataProviderImpl.SET_INTERVAL_METHOD_NAME;
+import static org.chromium.ui.dragdrop.DropDataProviderImpl.URI_PARAM;
import android.net.Uri;
import android.os.Bundle;
@@ -30,13 +30,13 @@
*/
static boolean clearImageCache(boolean imageIsInUse) {
Bundle bundle = new Bundle();
- bundle.putBoolean("imageIsInUse", imageIsInUse);
+ bundle.putBoolean(DropDataProviderImpl.IMAGE_USAGE_PARAM, imageIsInUse);
try {
ContextUtils.getApplicationContext()
.getContentResolver()
.call(DropDataProviderImpl.FULL_AUTH_URI, ON_DRAG_END_METHOD_NAME, "", bundle);
return true;
- } catch (NullPointerException | IllegalArgumentException exception) {
+ } catch (NullPointerException | IllegalArgumentException | SecurityException exception) {
return false;
}
}
@@ -50,7 +50,7 @@
.getContentResolver()
.call(DropDataProviderImpl.FULL_AUTH_URI, SET_INTERVAL_METHOD_NAME, "", bundle);
return true;
- } catch (NullPointerException | IllegalArgumentException exception) {
+ } catch (NullPointerException | IllegalArgumentException | SecurityException exception) {
return false;
}
}
@@ -60,11 +60,15 @@
* call the content provider.
*/
static @Nullable Uri cacheImageData(DropDataAndroid dropData) {
+ if (dropData.imageContent == null
+ || dropData.imageContentExtension == null
+ || dropData.imageFilename == null) {
+ return null;
+ }
Bundle bundle = new Bundle();
- bundle.putSerializable(DropDataProviderImpl.BYTES_PARAM, dropData.imageContent);
+ bundle.putByteArray(DropDataProviderImpl.BYTES_PARAM, dropData.imageContent);
bundle.putString(
DropDataProviderImpl.IMAGE_CONTENT_EXTENSION_PARAM, dropData.imageContentExtension);
-
bundle.putString(DropDataProviderImpl.IMAGE_FILE_PARAM, dropData.imageFilename);
try {
Bundle cachedUriBundle =
@@ -75,9 +79,11 @@
CACHE_METHOD_NAME,
"",
bundle);
- assumeNonNull(cachedUriBundle);
- return cachedUriBundle.getParcelable("uri");
- } catch (NullPointerException | IllegalArgumentException exception) {
+ if (cachedUriBundle == null) {
+ return null;
+ }
+ return cachedUriBundle.getParcelable(URI_PARAM);
+ } catch (NullPointerException | IllegalArgumentException | SecurityException exception) {
return null;
}
}
diff --git a/ui/android/junit/src/org/chromium/ui/dragdrop/DropDataProviderImplTest.java b/ui/android/junit/src/org/chromium/ui/dragdrop/DropDataProviderImplTest.java
index b4e0d9e..77f5484c 100644
--- a/ui/android/junit/src/org/chromium/ui/dragdrop/DropDataProviderImplTest.java
+++ b/ui/android/junit/src/org/chromium/ui/dragdrop/DropDataProviderImplTest.java
@@ -6,8 +6,22 @@
import static org.robolectric.Shadows.shadowOf;
+import static org.chromium.ui.dragdrop.DropDataProviderImpl.BYTES_PARAM;
+import static org.chromium.ui.dragdrop.DropDataProviderImpl.CACHE_METHOD_NAME;
+import static org.chromium.ui.dragdrop.DropDataProviderImpl.CLEAR_CACHE_PARAM;
+import static org.chromium.ui.dragdrop.DropDataProviderImpl.IMAGE_CONTENT_EXTENSION_PARAM;
+import static org.chromium.ui.dragdrop.DropDataProviderImpl.IMAGE_FILE_PARAM;
+import static org.chromium.ui.dragdrop.DropDataProviderImpl.IMAGE_USAGE_PARAM;
+import static org.chromium.ui.dragdrop.DropDataProviderImpl.ON_DRAG_END_METHOD_NAME;
+import static org.chromium.ui.dragdrop.DropDataProviderImpl.SET_INTERVAL_METHOD_NAME;
+import static org.chromium.ui.dragdrop.DropDataProviderImpl.URI_PARAM;
+
+import android.content.Context;
+import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Bundle;
+import android.os.Process;
import android.provider.OpenableColumns;
import android.webkit.MimeTypeMap;
@@ -18,8 +32,11 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.shadows.ShadowBinder;
+import org.robolectric.shadows.ShadowContentResolver;
import org.robolectric.shadows.ShadowLooper;
+import org.chromium.base.ContextUtils;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.test.BaseRobolectricTestRunner;
@@ -48,11 +65,21 @@
shadowOf(MimeTypeMap.getSingleton()).addExtensionMimeTypeMapping("jpg", "image/jpeg");
shadowOf(MimeTypeMap.getSingleton()).addExtensionMimeTypeMapping("gif", "image/gif");
shadowOf(MimeTypeMap.getSingleton()).addExtensionMimeTypeMapping("png", "image/png");
+
+ Context context = ContextUtils.getApplicationContext();
+ DropDataContentProvider provider = new DropDataContentProvider();
+ ProviderInfo providerInfo = new ProviderInfo();
+ providerInfo.authority = DropDataProviderImpl.FULL_AUTH_URI.getAuthority();
+ provider.attachInfo(context, providerInfo);
+ provider.setDropDataProviderImpl(mDropDataProviderImpl);
+ ShadowContentResolver.registerProviderInternal(
+ DropDataProviderImpl.FULL_AUTH_URI.getAuthority(), provider);
}
@After
public void tearDown() {
mDropDataProviderImpl.clearCache();
+ ShadowBinder.reset();
}
@Test
@@ -91,6 +118,12 @@
res = mDropDataProviderImpl.getStreamTypes(uri, "*/gif");
Assert.assertNull("res should be null when uri does not match the filter", res);
+
+ res = mDropDataProviderImpl.getStreamTypes(uri, "invalid");
+ Assert.assertNull("res should be null for filter without slash", res);
+
+ res = mDropDataProviderImpl.getStreamTypes(uri, "*");
+ Assert.assertNull("res should be null for wildcard without slash", res);
}
@Test
@@ -217,4 +250,166 @@
Assert.assertEquals(
errorMsg, expectedCnt, RecordHistogram.getHistogramTotalCountForTesting(histogram));
}
+
+ @Test
+ @SmallTest
+ public void testCall_mismatchedCallingUid() {
+ ShadowBinder.setCallingUid(Process.myUid() + 1);
+
+ Bundle cacheBundle = new Bundle();
+ cacheBundle.putByteArray(BYTES_PARAM, IMAGE_DATA_A);
+ cacheBundle.putString(IMAGE_CONTENT_EXTENSION_PARAM, EXTENSION_A);
+ cacheBundle.putString(IMAGE_FILE_PARAM, IMAGE_FILENAME_A);
+
+ Assert.assertThrows(
+ SecurityException.class,
+ () -> mDropDataProviderImpl.call(CACHE_METHOD_NAME, null, cacheBundle));
+
+ Bundle intervalBundle = new Bundle();
+ intervalBundle.putInt(CLEAR_CACHE_PARAM, CLEAR_CACHED_DATA_INTERVAL_MS);
+ Assert.assertThrows(
+ SecurityException.class,
+ () -> mDropDataProviderImpl.call(SET_INTERVAL_METHOD_NAME, null, intervalBundle));
+
+ Bundle dragEndBundle = new Bundle();
+ dragEndBundle.putBoolean(IMAGE_USAGE_PARAM, false);
+ Assert.assertThrows(
+ SecurityException.class,
+ () -> mDropDataProviderImpl.call(ON_DRAG_END_METHOD_NAME, null, dragEndBundle));
+ }
+
+ @Test
+ @SmallTest
+ public void testCall_cache_valid() {
+ Bundle bundle = new Bundle();
+ bundle.putByteArray(BYTES_PARAM, IMAGE_DATA_A);
+ bundle.putString(IMAGE_CONTENT_EXTENSION_PARAM, EXTENSION_A);
+ bundle.putString(IMAGE_FILE_PARAM, IMAGE_FILENAME_A);
+
+ Bundle result = mDropDataProviderImpl.call(CACHE_METHOD_NAME, null, bundle);
+ Assert.assertNotNull("Result bundle should not be null.", result);
+ Uri uri = result.getParcelable(URI_PARAM);
+ Assert.assertNotNull("URI should not be null.", uri);
+ Assert.assertEquals(
+ "MIME type should match.", "image/jpeg", mDropDataProviderImpl.getType(uri));
+ Assert.assertArrayEquals(
+ "Image bytes should match.",
+ IMAGE_DATA_A,
+ mDropDataProviderImpl.getImageBytesForTesting());
+ }
+
+ @Test
+ @SmallTest
+ public void testCall_setClearCachedDataIntervalMs_valid() throws FileNotFoundException {
+ Uri uri = mDropDataProviderImpl.cache(IMAGE_DATA_A, EXTENSION_A, IMAGE_FILENAME_A);
+ Bundle bundle = new Bundle();
+ bundle.putInt(CLEAR_CACHE_PARAM, CLEAR_CACHED_DATA_INTERVAL_MS);
+
+ mDropDataProviderImpl.call(SET_INTERVAL_METHOD_NAME, null, bundle);
+ Assert.assertEquals(
+ "Clear cache interval should be updated.",
+ CLEAR_CACHED_DATA_INTERVAL_MS,
+ mDropDataProviderImpl.getClearCachedDataIntervalMsForTesting());
+
+ mDropDataProviderImpl.onDragEnd(true);
+
+ ShadowLooper.idleMainLooper(CLEAR_CACHED_DATA_INTERVAL_MS - 1, TimeUnit.MILLISECONDS);
+ Assert.assertNotNull(
+ "Image bytes should not be null before interval elapsed.",
+ mDropDataProviderImpl.getImageBytesForTesting());
+
+ ShadowLooper.idleMainLooper(1, TimeUnit.MILLISECONDS);
+ Assert.assertNull(
+ "Image bytes should be null after interval elapsed.",
+ mDropDataProviderImpl.getImageBytesForTesting());
+ }
+
+ @Test
+ @SmallTest
+ public void testCall_onDragEnd_valid() {
+ mDropDataProviderImpl.cache(IMAGE_DATA_A, EXTENSION_A, IMAGE_FILENAME_A);
+ Assert.assertNotNull(
+ "Image bytes should not be null before drag end.",
+ mDropDataProviderImpl.getImageBytesForTesting());
+
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(IMAGE_USAGE_PARAM, false);
+
+ mDropDataProviderImpl.call(ON_DRAG_END_METHOD_NAME, null, bundle);
+ Assert.assertNull(
+ "Image bytes should be null after immediate drag end.",
+ mDropDataProviderImpl.getImageBytesForTesting());
+ }
+
+ @Test
+ @SmallTest
+ public void testCall_nullOrInvalidParameters() {
+ Assert.assertNull(
+ "Result should be null for null method.",
+ mDropDataProviderImpl.call(null, null, new Bundle()));
+ Assert.assertNull(
+ "Result should be null for unknown method.",
+ mDropDataProviderImpl.call("unknownMethod", null, new Bundle()));
+ Assert.assertNull(
+ "Result should be null for null extras.",
+ mDropDataProviderImpl.call(CACHE_METHOD_NAME, null, null));
+ Assert.assertNull(
+ "Result should be null for null extras.",
+ mDropDataProviderImpl.call(SET_INTERVAL_METHOD_NAME, null, null));
+ Assert.assertNull(
+ "Result should be null for null extras.",
+ mDropDataProviderImpl.call(ON_DRAG_END_METHOD_NAME, null, null));
+
+ Bundle bundleNoBytes = new Bundle();
+ bundleNoBytes.putString(IMAGE_CONTENT_EXTENSION_PARAM, EXTENSION_A);
+ bundleNoBytes.putString(IMAGE_FILE_PARAM, IMAGE_FILENAME_A);
+ Assert.assertNull(
+ "Result should be null when byte array is missing.",
+ mDropDataProviderImpl.call(CACHE_METHOD_NAME, null, bundleNoBytes));
+
+ Bundle bundleNoExtension = new Bundle();
+ bundleNoExtension.putByteArray(BYTES_PARAM, IMAGE_DATA_A);
+ bundleNoExtension.putString(IMAGE_FILE_PARAM, IMAGE_FILENAME_A);
+ Assert.assertNull(
+ "Result should be null when extension is missing.",
+ mDropDataProviderImpl.call(CACHE_METHOD_NAME, null, bundleNoExtension));
+
+ Bundle bundleNoFilename = new Bundle();
... (file diff truncated)
Original Bug Report
Missing caller validation in DropDataContentProvider.call() allows unsafe deserialization
Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: The DropDataContentProvider class overrides ContentProvider.call() without verifying the caller’s identity or permissions. A local malicious application holding a temporary URI permission grant can potentially access this endpoint and invoke unsafe Java deserialization via getSerializable(). This could allow an attacker to execute arbitrary code within the context of the unsandboxed Chrome browser process.
Affected files:
ui/android/java/src/org/chromium/ui/dragdrop/DropDataContentProvider.javaui/android/java/src/org/chromium/ui/dragdrop/DropDataProviderImpl.javaui/android/java/src/org/chromium/ui/dragdrop/DragAndDropDelegateImpl.javachrome/android/java/AndroidManifest.xml
Estimated timestamp from git blame: 2022-10-06
Potential Security Vulnerability in DropDataContentProvider
1. Root Cause
In ui/android/java/src/org/chromium/ui/dragdrop/DropDataContentProvider.java (lines 97-99), the call() method is overridden and delegates directly to the underlying implementation without validating the caller:
@Override
public @Nullable Bundle call(String method, @Nullable String arg, @Nullable Bundle extras) {
return mDropDataProviderImpl.call(method, arg, extras);
}
There are no validation checks (such as verifying the calling UID or package identity) performed before processing the method.
Inside ui/android/java/src/org/chromium/ui/dragdrop/DropDataProviderImpl.java (lines 361-365), the call method dispatches incoming commands. Under the CACHE_METHOD_NAME case ("cache"), it extracts the payload using a deprecated, untyped getSerializable call:
case CACHE_METHOD_NAME:
assumeNonNull(extras);
byte[] imageBytes = (byte[]) extras.getSerializable(BYTES_PARAM);
Calling getSerializable(String) without specifying an explicit target class triggers untyped deserialization. If the object associated with the key is a serialized Java object, the Android OS unmarshalling engine will instantiate the class using ObjectInputStream.readObject(). Because this executes inside Chrome’s browser process, Chrome’s full classpath is available to resolve potential deserialization gadget chains.
2. Potential Attack Flow
Although DropDataContentProvider is declared with android:exported="false" in chrome/android/java/AndroidManifest.xml (lines 1427-1431), it is configured with android:grantUriPermissions="true". An attacker could potentially bypass the export restriction and invoke call() via the following steps:
- User Gesture and Drag-and-Drop Initiation: A user initiates a drag-and-drop gesture on an image (
<img>) in Chrome and drops it onto a malicious local application registered as a drop target. - Temporary URI Grant Emission: During the drag,
DragAndDropDelegateImpl.java(lines 271-278) caches the image and generates a content URI pointing to the provider. The drag flags (View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ) cause the Android OS to allocate a temporary URI read permission to the drop-target app. - Acquiring the Binder: The malicious application accepts the drop event and requests the drag-and-drop permission grant. Because the app holds a valid temporary URI grant, Android’s
ActivityManagerServiceallows it to resolve the provider’s authority and obtain theIContentProviderBinder interface. - Bypassing Export Restrictions: The Android framework does not enforce read/write permission or export restrictions on
ContentProvider.call()transactions. Once the binder is acquired, the malicious app can invoke thecall()method directly. - Triggering Deserialization: The malicious application calls
ContentResolver.call(...)with the method name"cache"and a craftedBundlecontaining a serialized gadget payload under the key"bytes". WhenDropDataProviderImplretrieves the extra viagetSerializable(), the payload is deserialized in Chrome’s unsandboxed browser process.
Note: These are suggested/potential steps; our tooling does not currently have the capability to execute code or verify the existence of specific gadget chains on Chrome’s classpath.
3. Recommended Remediation
To address this potential issue, the following changes are recommended:
- Caller Identity Validation: Restrict access to
DropDataContentProvider.call()by validating that the calling UID matches Chrome’s own UID. Since this provider’scall()method is designed purely for internal loopback communication (viaDropDataProviderUtils.java), it should only accept calls from Chrome itself:if (Binder.getCallingUid() != android.os.Process.myUid()) { throw new SecurityException("Unauthorized caller"); } - Type-Safe Bundle Access: Instead of passing the raw image bytes as a
Serializableobject and deserializing it viagetSerializable(), pass it as a standard byte array (byte[]) and retrieve it usingextras.getByteArray(BYTES_PARAM). This avoids invokingObjectInputStreamentirely.
Evaluated with Chrome root at commit: e9507a33bb4148ee071aaaf8a7e9ad68770359bf
Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:
- If you are familiar with the severity guidelines, you may adjust the severity.
- If this is a false positive, and there’s no work to be done, please close as WAI.
- If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.
Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.