Medium chrome Authorization Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactIncorrect authorization in Sync
DescriptionIncorrect authorization in Sync
ComponentSync
Sub-componentSync
Bug ClassAuthorization Error
Tracker496623893
Fix commitf6e9fd71c2de (chromium/src) +4/-117
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-06

Files Changed

  • components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
  • components/sync/nigori/nigori_sync_bridge_impl.cc
  • chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
From f6e9fd71c2ded7e93488a7545cb104ee73bfb027 Mon Sep 17 00:00:00 2001
From: Mikel Astiz <mastiz@chromium.org>
Date: Tue, 01 Sep 2026 06:30:09 -0700
Subject: [PATCH] [sync] Disallow transitioning from trusted vault to keystore

There are no actual plans to make use of this transition, so the code
can be simplified, bypassing the need to audit and fix scenarios where
this transition should be forbidden more selectively.

Fixed: 496623893
Change-Id: I90131e24f8f1470bb4bbac1d0e653d001bbb704a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8336250
Reviewed-by: Maksim Moskvitin <mmoskvitin@google.com>
Commit-Queue: Mikel Astiz <mastiz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1689803}
---
diff --git a/components/sync/nigori/nigori_sync_bridge_impl.cc b/components/sync/nigori/nigori_sync_bridge_impl.cc
index add4726..43c834b 100644
--- a/components/sync/nigori/nigori_sync_bridge_impl.cc
+++ b/components/sync/nigori/nigori_sync_bridge_impl.cc
@@ -204,8 +204,7 @@
     case NigoriSpecifics::CUSTOM_PASSPHRASE:
       return false;
     case NigoriSpecifics::TRUSTED_VAULT_PASSPHRASE:
-      return new_passphrase_type == NigoriSpecifics::CUSTOM_PASSPHRASE ||
-             new_passphrase_type == NigoriSpecifics::KEYSTORE_PASSPHRASE;
+      return new_passphrase_type == NigoriSpecifics::CUSTOM_PASSPHRASE;
   }
   NOTREACHED();
 }
diff --git a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
index 8e90274..79c30e6e 100644
--- a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
@@ -1701,69 +1701,6 @@
 
 IN_PROC_BROWSER_TEST_P(
     SingleClientNigoriWithWebApiTest,
-    ShouldRemotelyTransitFromTrustedVaultToKeystorePassphrase) {
-  // Mimic the account being already using a trusted vault passphrase.
-  SetNigoriInFakeServer(BuildTrustedVaultNigoriSpecifics({kTestEncryptionKey}),
-                        GetFakeServer());
-
-  ASSERT_TRUE(SetupSync());
-  ASSERT_TRUE(GetSyncService(0)
-                  ->GetUserSettings()
-                  ->IsTrustedVaultKeyRequiredForPreferredDataTypes());
-  ASSERT_FALSE(GetSyncService(0)->GetActiveDataTypes().Has(syncer::PASSWORDS));
-
-  // There needs to be an existing tab for the second tab (the retrieval flow)
-  // to be closeable via javascript.
-  chrome::AddTabAt(GetBrowser(0), GURL(url::kAboutBlankURL), /*index=*/0,
-                   /*foreground=*/true);
-
-  // Mimic opening a web page where the user can interact with the retrieval
-  // flow.
-  OpenTabForSyncKeyRetrieval(
-      GetBrowser(0),
-      trusted_vault::TrustedVaultUserActionTriggerForUMA::kProfileMenu);
-  ASSERT_THAT(GetBrowser(0)->tab_strip_model()->GetActiveWebContents(),
-              NotNull());
-
-  // Wait until the page closes, which indicates successful completion.
-  EXPECT_TRUE(
-      TabClosedChecker(GetBrowser(0)->tab_strip_model()->GetActiveWebContents())
-          .Wait());
-
-  // Mimic remote transition to keystore passphrase.
-  const std::vector<std::vector<uint8_t>>& keystore_keys =
-      GetFakeServer()->GetKeystoreKeys();
-  ASSERT_THAT(keystore_keys, SizeIs(1));
-  const KeyParamsForTesting kKeystoreKeyParams =
-      KeystoreKeyParamsForTesting(keystore_keys.back());
-  const KeyParamsForTesting kTrustedVaultKeyParams =
-      TrustedVaultKeyParamsForTesting(kTestEncryptionKey);
-  SetNigoriInFakeServer(
-      BuildKeystoreNigoriSpecifics(
-          /*keybag_keys_params=*/{kTrustedVaultKeyParams, kKeystoreKeyParams},
-          /*keystore_decryptor_params*/ {kKeystoreKeyParams},
-          /*keystore_key_params=*/kKeystoreKeyParams),
-      GetFakeServer());
-
-  // Ensure that client can decrypt with both |kTrustedVaultKeyParams|
-  // and |kKeystoreKeyParams|.
-  const password_manager::PasswordForm password_form1 =
-      passwords_helper::CreateTestPasswordForm(1, GetPasswordStoreType());
-  const password_manager::PasswordForm password_form2 =
-      passwords_helper::CreateTestPasswordForm(2, GetPasswordStoreType());
-
-  passwords_helper::InjectEncryptedServerPassword(
-      password_form1, kKeystoreKeyParams.password,
-      kKeystoreKeyParams.derivation_params, GetFakeServer());
-  passwords_helper::InjectEncryptedServerPassword(
-      password_form2, kTrustedVaultKeyParams.password,
-      kTrustedVaultKeyParams.derivation_params, GetFakeServer());
-
-  EXPECT_TRUE(WaitForPasswordForms({password_form1, password_form2}));
-}
-
-IN_PROC_BROWSER_TEST_P(
-    SingleClientNigoriWithWebApiTest,
     ShouldRemotelyTransitFromTrustedVaultToCustomPassphrase) {
   // Mimic the account being already using a trusted vault passphrase.
   SetNigoriInFakeServer(BuildTrustedVaultNigoriSpecifics({kTestEncryptionKey}),
diff --git a/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc b/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
index ec747181..9d871b9 100644
--- a/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
+++ b/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
@@ -1535,9 +1535,9 @@
 }
 
 // Tests processing of remote incremental update that transits from trusted
-// vault to keystore passphrase.
+// vault to keystore passphrase. The bridge should report model error.
 TEST_F(NigoriSyncBridgeImplTest,
-       ShouldProcessRemoteTransitionFromTrustedVaultToKeystore) {
+       ShouldFailOnRemoteTransitionFromTrustedVaultToKeystore) {
   EntityData entity_data;
   *entity_data.specifics.mutable_nigori() =
       BuildTrustedVaultNigoriSpecifics({kTrustedVaultKey});
@@ -1564,23 +1564,8 @@
       /*keystore_decryptor_params=*/kKeystoreKeyParams,
       /*keystore_key_params=*/kKeystoreKeyParams);
 
-  EXPECT_CALL(*observer(), OnEncryptedTypesChanged).Times(0);
-  EXPECT_CALL(*observer(), OnCryptographerStateChanged(
-                               NotNull(), /*has_pending_keys=*/false));
-  EXPECT_CALL(
-      *observer(),
-      OnPassphraseTypeChanged(PassphraseType::kKeystorePassphrase, NullTime()));
-
   EXPECT_THAT(bridge()->ApplyIncrementalSyncChanges(std::move(new_entity_data)),
-              Eq(std::nullopt));
-  EXPECT_THAT(bridge()->GetPassphraseType(),
-              Eq(PassphraseType::kKeystorePassphrase));
-  EXPECT_THAT(bridge()->GetEncryptedTypes(), Eq(AlwaysEncryptedUserTypes()));
-  EXPECT_FALSE(bridge()->HasPendingKeysForTesting());
-
-  EXPECT_THAT(*cryptographer(), CanDecryptWith(kTrustedVaultKeyParams));
-  EXPECT_THAT(*cryptographer(), CanDecryptWith(kKeystoreKeyParams));
-  EXPECT_THAT(*cryptographer(), HasDefaultKeyDerivedFrom(kKeystoreKeyParams));
+              Ne(std::nullopt));
 }
 
 TEST_F(NigoriSyncBridgeImplTest,
@@ -1688,40 +1673,6 @@
 }
 
 // Tests processing of remote incremental update that transits from trusted
-// vault to keystore passphrase, which doesn't contain trusted vault key. The
-// bridge should report model error.
-TEST_F(NigoriSyncBridgeImplTest,
-       ShouldFailOnInvalidRemoteTransitionFromTrustedVaultToKeystore) {
-  EntityData entity_data;
-  *entity_data.specifics.mutable_nigori() =
-      BuildTrustedVaultNigoriSpecifics({kTrustedVaultKey});
-
-  ASSERT_TRUE(bridge()->SetKeystoreKeys({kRawKeystoreKey}));
-  ASSERT_THAT(bridge()->MergeFullSyncData(std::move(entity_data)),
-              Eq(std::nullopt));
-  bridge()->NotifyInitialStateToObservers();
-  ASSERT_TRUE(bridge()->HasPendingKeysForTesting());
-  bridge()->AddTrustedVaultDecryptionKeys({kTrustedVaultKey});
-  ASSERT_FALSE(bridge()->HasPendingKeysForTesting());
-  ASSERT_THAT(bridge()->GetPassphraseType(),
-              Eq(PassphraseType::kTrustedVaultPassphrase));
-  ASSERT_THAT(bridge()->GetDataForDebugging(),
-              Not(HasCustomPassphraseNigori()));
-
-  const KeyParamsForTesting kKeystoreKeyParams =
-      KeystoreKeyParamsForTesting(kRawKeystoreKey);
-  // Don't populate kTrustedVaultKey into `new_entity_data`.
-  EntityData new_entity_data;
-  *new_entity_data.specifics.mutable_nigori() = BuildKeystoreNigoriSpecifics(
-      /*keybag_keys_params=*/{kKeystoreKeyParams},
-      /*keystore_decryptor_params=*/kKeystoreKeyParams,
-      /*keystore_key_params=*/kKeystoreKeyParams);
-
-  EXPECT_THAT(bridge()->ApplyIncrementalSyncChanges(std::move(new_entity_data)),
-              Ne(std::nullopt));
-}
-
-// Tests processing of remote incremental update that transits from trusted
 // vault to custom passphrase, which doesn't contain trusted vault key. The
 // bridge should report model error.
 TEST_F(NigoriSyncBridgeImplTest,
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
index 8e90274..79c30e6e 100644
--- a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
@@ -1701,69 +1701,6 @@
 
 IN_PROC_BROWSER_TEST_P(
     SingleClientNigoriWithWebApiTest,
-    ShouldRemotelyTransitFromTrustedVaultToKeystorePassphrase) {
-  // Mimic the account being already using a trusted vault passphrase.
-  SetNigoriInFakeServer(BuildTrustedVaultNigoriSpecifics({kTestEncryptionKey}),
-                        GetFakeServer());
-
-  ASSERT_TRUE(SetupSync());
-  ASSERT_TRUE(GetSyncService(0)
-                  ->GetUserSettings()
-                  ->IsTrustedVaultKeyRequiredForPreferredDataTypes());
-  ASSERT_FALSE(GetSyncService(0)->GetActiveDataTypes().Has(syncer::PASSWORDS));
-
-  // There needs to be an existing tab for the second tab (the retrieval flow)
-  // to be closeable via javascript.
-  chrome::AddTabAt(GetBrowser(0), GURL(url::kAboutBlankURL), /*index=*/0,
-                   /*foreground=*/true);
-
-  // Mimic opening a web page where the user can interact with the retrieval
-  // flow.
-  OpenTabForSyncKeyRetrieval(
-      GetBrowser(0),
-      trusted_vault::TrustedVaultUserActionTriggerForUMA::kProfileMenu);
-  ASSERT_THAT(GetBrowser(0)->tab_strip_model()->GetActiveWebContents(),
-              NotNull());
-
-  // Wait until the page closes, which indicates successful completion.
-  EXPECT_TRUE(
-      TabClosedChecker(GetBrowser(0)->tab_strip_model()->GetActiveWebContents())
-          .Wait());
-
-  // Mimic remote transition to keystore passphrase.
-  const std::vector<std::vector<uint8_t>>& keystore_keys =
-      GetFakeServer()->GetKeystoreKeys();
-  ASSERT_THAT(keystore_keys, SizeIs(1));
-  const KeyParamsForTesting kKeystoreKeyParams =
-      KeystoreKeyParamsForTesting(keystore_keys.back());
-  const KeyParamsForTesting kTrustedVaultKeyParams =
-      TrustedVaultKeyParamsForTesting(kTestEncryptionKey);
-  SetNigoriInFakeServer(
-      BuildKeystoreNigoriSpecifics(
-          /*keybag_keys_params=*/{kTrustedVaultKeyParams, kKeystoreKeyParams},
-          /*keystore_decryptor_params*/ {kKeystoreKeyParams},
-          /*keystore_key_params=*/kKeystoreKeyParams),
-      GetFakeServer());
-
-  // Ensure that client can decrypt with both |kTrustedVaultKeyParams|
-  // and |kKeystoreKeyParams|.
-  const password_manager::PasswordForm password_form1 =
-      passwords_helper::CreateTestPasswordForm(1, GetPasswordStoreType());
-  const password_manager::PasswordForm password_form2 =
-      passwords_helper::CreateTestPasswordForm(2, GetPasswordStoreType());
-
-  passwords_helper::InjectEncryptedServerPassword(
-      password_form1, kKeystoreKeyParams.password,
-      kKeystoreKeyParams.derivation_params, GetFakeServer());
-  passwords_helper::InjectEncryptedServerPassword(
-      password_form2, kTrustedVaultKeyParams.password,
-      kTrustedVaultKeyParams.derivation_params, GetFakeServer());
-
-  EXPECT_TRUE(WaitForPasswordForms({password_form1, password_form2}));
-}
-
-IN_PROC_BROWSER_TEST_P(
-    SingleClientNigoriWithWebApiTest,
     ShouldRemotelyTransitFromTrustedVaultToCustomPassphrase) {
   // Mimic the account being already using a trusted vault passphrase.
   SetNigoriInFakeServer(BuildTrustedVaultNigoriSpecifics({kTestEncryptionKey}),
diff --git a/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc b/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
index ec747181..9d871b9 100644
--- a/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
+++ b/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
@@ -1535,9 +1535,9 @@
 }
 
 // Tests processing of remote incremental update that transits from trusted
-// vault to keystore passphrase.
+// vault to keystore passphrase. The bridge should report model error.
 TEST_F(NigoriSyncBridgeImplTest,
-       ShouldProcessRemoteTransitionFromTrustedVaultToKeystore) {
+       ShouldFailOnRemoteTransitionFromTrustedVaultToKeystore) {
   EntityData entity_data;
   *entity_data.specifics.mutable_nigori() =
       BuildTrustedVaultNigoriSpecifics({kTrustedVaultKey});
@@ -1564,23 +1564,8 @@
       /*keystore_decryptor_params=*/kKeystoreKeyParams,
       /*keystore_key_params=*/kKeystoreKeyParams);
 
-  EXPECT_CALL(*observer(), OnEncryptedTypesChanged).Times(0);
-  EXPECT_CALL(*observer(), OnCryptographerStateChanged(
-                               NotNull(), /*has_pending_keys=*/false));
-  EXPECT_CALL(
-      *observer(),
-      OnPassphraseTypeChanged(PassphraseType::kKeystorePassphrase, NullTime()));
-
   EXPECT_THAT(bridge()->ApplyIncrementalSyncChanges(std::move(new_entity_data)),
-              Eq(std::nullopt));
-  EXPECT_THAT(bridge()->GetPassphraseType(),
-              Eq(PassphraseType::kKeystorePassphrase));
-  EXPECT_THAT(bridge()->GetEncryptedTypes(), Eq(AlwaysEncryptedUserTypes()));
-  EXPECT_FALSE(bridge()->HasPendingKeysForTesting());
-
-  EXPECT_THAT(*cryptographer(), CanDecryptWith(kTrustedVaultKeyParams));
-  EXPECT_THAT(*cryptographer(), CanDecryptWith(kKeystoreKeyParams));
-  EXPECT_THAT(*cryptographer(), HasDefaultKeyDerivedFrom(kKeystoreKeyParams));
+              Ne(std::nullopt));
 }
 
 TEST_F(NigoriSyncBridgeImplTest,
@@ -1688,40 +1673,6 @@
 }
 
 // Tests processing of remote incremental update that transits from trusted
-// vault to keystore passphrase, which doesn't contain trusted vault key. The
-// bridge should report model error.
-TEST_F(NigoriSyncBridgeImplTest,
-       ShouldFailOnInvalidRemoteTransitionFromTrustedVaultToKeystore) {
-  EntityData entity_data;
-  *entity_data.specifics.mutable_nigori() =
-      BuildTrustedVaultNigoriSpecifics({kTrustedVaultKey});
-
-  ASSERT_TRUE(bridge()->SetKeystoreKeys({kRawKeystoreKey}));
-  ASSERT_THAT(bridge()->MergeFullSyncData(std::move(entity_data)),
-              Eq(std::nullopt));
-  bridge()->NotifyInitialStateToObservers();
-  ASSERT_TRUE(bridge()->HasPendingKeysForTesting());
-  bridge()->AddTrustedVaultDecryptionKeys({kTrustedVaultKey});
-  ASSERT_FALSE(bridge()->HasPendingKeysForTesting());
-  ASSERT_THAT(bridge()->GetPassphraseType(),
-              Eq(PassphraseType::kTrustedVaultPassphrase));
-  ASSERT_THAT(bridge()->GetDataForDebugging(),
-              Not(HasCustomPassphraseNigori()));
-
-  const KeyParamsForTesting kKeystoreKeyParams =
-      KeystoreKeyParamsForTesting(kRawKeystoreKey);
-  // Don't populate kTrustedVaultKey into `new_entity_data`.
-  EntityData new_entity_data;
-  *new_entity_data.specifics.mutable_nigori() = BuildKeystoreNigoriSpecifics(
-      /*keybag_keys_params=*/{kKeystoreKeyParams},
-      /*keystore_decryptor_params=*/kKeystoreKeyParams,
-      /*keystore_key_params=*/kKeystoreKeyParams);
-
-  EXPECT_THAT(bridge()->ApplyIncrementalSyncChanges(std::move(new_entity_data)),
-              Ne(std::nullopt));
-}
-
-// Tests processing of remote incremental update that transits from trusted
 // vault to custom passphrase, which doesn't contain trusted vault key. The
 // bridge should report model error.
 TEST_F(NigoriSyncBridgeImplTest,
Loading diff…

Original Bug Report

reported by vi...@google.com

Trusted Vault Key Leak via KEYSTORE downgrade in NigoriSyncBridgeImpl

Project Fortify, an experimental security project, has identified the following potential security issue.

Overview: A logic flaw in NigoriSyncBridgeImpl allows an attacker with compromised Gaia credentials to bypass cryptographic protections and leak the victim’s hardware-backed Trusted Vault keys. A benign sync update can clear a critical security guard (last_default_trusted_vault_key_name), allowing a subsequent malicious update to downgrade the encryption mode to Keystore. The device then re-encrypts its entire keybag, including the Trusted Vault keys, with a Keystore key known to the attacker, leaking the keys.

Affected files:

  • components/sync/nigori/nigori_sync_bridge_impl.cc

Estimated timestamp from git blame: 2024-11-14

Description

There is a potential high-severity logic vulnerability in NigoriSyncBridgeImpl that could allow an attacker who has compromised a user’s Gaia credentials to bypass Trusted Vault protections and extract the victim’s hardware-backed Trusted Vault keys. This defeats the explicit threat model of Trusted Vault, which is designed to protect sensitive synced data (like passwords) even if the primary account credentials are stolen.

The vulnerability stems from the mismanagement of the last_default_trusted_vault_key_name guard. This guard is intended to prevent unauthorized downgrades from TRUSTED_VAULT_PASSPHRASE to KEYSTORE_PASSPHRASE by ensuring that any new keybag from the server still contains the victim’s Trusted Vault key (see components/sync/nigori/nigori_sync_bridge_impl.cc lines 870-875).

However, in components/sync/nigori/nigori_sync_bridge_impl.cc, the function TryDecryptPendingKeysWith unconditionally clears this guard on line 908 upon a successful decryption:

905:   // Reset `last_default_trusted_vault_key_name` as `state_` might go out of
906:   // TRUSTED_VAULT passphrase type. The callers are responsible to set it again
907:   // if needed.
908:   state_.last_default_trusted_vault_key_name = std::nullopt;

The primary caller of this function, UpdateLocalState (at line 773), completely fails to restore last_default_trusted_vault_key_name if the passphrase type remains TRUSTED_VAULT_PASSPHRASE. This leaves the local state persisted to disk without its intended downgrade protection.

Potential Attack Scenario

(Note: These are suggested steps; Fortify LLM agent does not yet have the ability to run code to verify this end-to-end.)

  1. Initial State: The victim is using Trusted Vault. Their local state_.last_default_trusted_vault_key_name is set, and their hardware-backed Trusted Vault key is in their CryptographerImpl keybag.
  2. Attacker Compromises Gaia: The attacker gains access to the victim’s Google account but cannot access the hardware-backed Trusted Vault keys directly.
  3. Clear the Guard: The attacker pushes a benign NigoriSpecifics update to the sync server (e.g., changing a non-cryptographic field like trusted_vault_debug_info), keeping the TRUSTED_VAULT_PASSPHRASE type.
  4. Device Processes Benign Update: The victim’s browser receives this update. UpdateLocalState calls TryDecryptPendingKeysWith, which successfully decrypts the update using the existing Trusted Vault key. This clears state_.last_default_trusted_vault_key_name (line 908). UpdateLocalState returns without restoring the guard, and the unprotected state is saved to disk.
  5. Trigger Keystore Downgrade: The attacker then pushes a malicious KEYSTORE_PASSPHRASE update, providing a new keybag encrypted with a Keystore key (which the attacker possesses due to their Gaia access). This keybag does not contain the victim’s Trusted Vault keys.
  6. Downgrade Succeeds: The victim’s device receives the malicious update. UpdateLocalState calls TryDecryptPendingKeysWith. Because the guard was cleared in step 4, the check at lines 870-875 (has_value() is false) is entirely bypassed. The device accepts the downgrade to Keystore mode. Crucially, the old Trusted Vault keys remain in the local cryptographer->key_bag_.
  7. Key Extraction: Upon a browser restart or keystore key rotation, NeedsKeystoreReencryption triggers. The device re-encrypts its entire keybag (which still contains the Trusted Vault keys) using the attacker-controlled Keystore key and uploads it to the sync server. The attacker fetches this update, decrypts it, extracts the hardware-backed Trusted Vault keys, and can now decrypt all of the victim’s historical synced data.

Suggested Fix

In NigoriSyncBridgeImpl::UpdateLocalState, after the call to TryDecryptPendingKeysWith(decryption_key_bag_for_remote_update), the code must check if the current state_.passphrase_type is TRUSTED_VAULT_PASSPHRASE. If it is, it should restore state_.last_default_trusted_vault_key_name using state_.cryptographer->GetDefaultEncryptionKeyName(), similar to what is done in AddTrustedVaultDecryptionKeys.

Evaluated with Chrome root at commit: bb48272cafb7e24c93f55ef40da398cd206ee651


Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve accuracy over time. Please feel free to reach out to me if you have concerns or feedback.

View on issue tracker