| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ash/extensions/extensions_permissions_tracker.h" |
| |
| #include "base/containers/fixed_flat_set.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/common/pref_names.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/pref_service.h" |
| #include "extensions/browser/pref_names.h" |
| #include "extensions/common/permissions/api_permission_set.h" |
| #include "extensions/common/permissions/manifest_permission_set.h" |
| #include "extensions/common/permissions/permission_set.h" |
| #include "extensions/common/permissions/permissions_data.h" |
| |
| namespace extensions { |
| |
| namespace { |
| |
| // Apps/extensions explicitly allowlisted for skipping warnings for MGS (Managed |
| // guest sessions) users. |
| constexpr auto kManagedGuestSessionAllowlist = base::MakeFixedFlatSet< |
| std::string_view>({ |
| // Managed guest sessions in general: |
| "cbkkbcmdlboombapidmoeolnmdacpkch", // Chrome RDP |
| "inomeogfingihgjfjlpeplalcfajhgai", // Chrome Remote Desktop |
| "djflhoibgkdhkhhcedjiklpkjnoahfmg", // User Agent Switcher |
| "iabmpiboiopbgfabjmgeedhcmjenhbla", // VNC Viewer |
| "haiffjcadagjlijoggckpgfnoeiflnem", // Citrix Receiver |
| "lfnfbcjdepjffcaiagkdmlmiipelnfbb", // Citrix Receiver (branded) |
| "mfaihdlpglflfgpfjcifdjdjcckigekc", // ARC Runtime |
| "ngjnkanfphagcaokhjecbgkboelgfcnf", // Print button |
| "cjanmonomjogheabiocdamfpknlpdehm", // HP printer driver |
| "ioofdkhojeeimmagbjbknkejkgbphdfl", // RICOH Print for Chrome |
| "pmnllmkmjilbojkpgplbdmckghmaocjh", // Scan app by François Beaufort |
| "haeblkpifdemlfnkogkipmghfcbonief", // DriveLock Smart Card Middleware |
| "mpnkhdpphjiihmlmkcamhpogecnnfffa", // Service NSW Kiosk Utility |
| "npilppbicblkkgjfnbmibmhhgjhobpll", // QwickACCESS |
| // TODO(isandrk): Only on the allowlist for the purpose of getting the soft |
| // MGS warning. Remove once dynamic MGS warnings are implemented. |
| "ppkfnjlimknmjoaemnpidmdlfchhehel", // VMware Horizon Client for Chrome |
| |
| // Libraries: |
| "aclofikceldphonlfmghmimkodjdmhck", // Ancoris login component |
| "eilbnahdgoddoedakcmfkcgfoegeloil", // Ancoris proxy component |
| "ceehlgckkmkaoggdnjhibffkphfnphmg", // Libdata login |
| "fnhgfoccpcjdnjcobejogdnlnidceemb", // OverDrive |
| |
| // Education: |
| "cmeclblmdmffdgpdlifgepjddoplmmal", // Imagine Learning |
| |
| // Retail mode: |
| "bjfeaefhaooblkndnoabbkkkenknkemb", // 500 px demo |
| "ehcabepphndocfmgbdkbjibfodelmpbb", // Angry Birds demo |
| "kgimkbnclbekdkabkpjhpakhhalfanda", // Bejeweled demo |
| "joodangkbfjnajiiifokapkpmhfnpleo", // Calculator |
| "fpgfohogebplgnamlafljlcidjedbdeb", // Calendar demo |
| "jkoildpomkimndcphjpffmephmcmkfhn", // Chromebook Demo App |
| "lbhdhapagjhalobandnbdnmblnmocojh", // Crackle demo |
| "ielkookhdphmgbipcfmafkaiagademfp", // Custom bookmarks |
| "kogjlbfgggambihdjcpijgcbmenblimd", // Custom bookmarks |
| "ogbkmlkceflgpilgbmbcfbifckpkfacf", // Custom bookmarks |
| "pbbbjjecobhljkkcenlakfnkmkfkfamd", // Custom bookmarks |
| "jkbfjmnjcdmhlfpephomoiipbhcoiffb", // Custom bookmarks |
| "dgmblbpgafgcgpkoiilhjifindhinmai", // Custom bookmarks |
| "iggnealjakkgfofealilhkkclnbnfnmo", // Custom bookmarks |
| "lplkobnahgbopmpkdapaihnnojkphahc", // Custom bookmarks |
| "lejnflfhjpcannpaghnahbedlabpmhoh", // Custom bookmarks |
| "dhjmfhojkfjmfbnbnpichdmcdghdpccg", // Cut the Rope demo |
| "ebkhfdfghngbimnpgelagnfacdafhaba", // Deezer demo |
| "npnjdccdffhdndcbeappiamcehbhjibf", // Docs.app demo |
| "ekgadegabdkcbkodfbgidncffijbghhl", // Duolingo demo |
| "iddohohhpmajlkbejjjcfednjnhlnenk", // Evernote demo |
| "bjdhhokmhgelphffoafoejjmlfblpdha", // Gmail demo |
| "nldmakcnfaflagmohifhcihkfgcbmhph", // Gmail offline demo |
| "mdhnphfgagkpdhndljccoackjjhghlif", // Google Drive demo |
| "dondgdlndnpianbklfnehgdhkickdjck", // Google Keep demo |
| "amfoiggnkefambnaaphodjdmdooiinna", // Google Play Movie and TV demo |
| "fgjnkhlabjcaajddbaenilcmpcidahll", // Google+ demo |
| "ifpkhncdnjfipfjlhfidljjffdgklanh", // Google+ Photos demo |
| "cgmlfbhkckbedohgdepgbkflommbfkep", // Hangouts.app demo |
| "ndlgnmfmgpdecjgehbcejboifbbmlkhp", // Hash demo |
| "edhhaiphkklkcfcbnlbpbiepchnkgkpn", // Helper.extension demo |
| "jckncghadoodfbbbmbpldacojkooophh", // Journal demo |
| "diehajhcjifpahdplfdkhiboknagmfii", // Kindle demo |
| "idneggepppginmaklfbaniklagjghpio", // Kingsroad demo |
| "nhpmmldpbfjofkipjaieeomhnmcgihfm", // Menu.app demo |
| "kcjbmmhccecjokfmckhddpmghepcnidb", // Mint demo |
| "onbhgdmifjebcabplolilidlpgeknifi", // Music.app demo |
| "kkkbcoabfhgekpnddfkaphobhinociem", // Netflix demo |
| "adlphlfdhhjenpgimjochcpelbijkich", // New York Times demo |
| "cgefhjmlaifaamhhoojmpcnihlbddeki", // Pandora demo |
| "kpjjigggmcjinapdeipapdcnmnjealll", // Pixlr demo |
| "ifnadhpngkodeccijnalokiabanejfgm", // Pixsta demo |
| "klcojgagjmpgmffcildkgbfmfffncpcd", // Plex demo |
| "nnikmgjhdlphciaonjmoppfckbpoinnb", // Pocket demo |
| "khldngaiohpnnoikfmnmfnebecgeobep", // Polarr Photo demo |
| "aleodiobpjillgfjdkblghiiaegggmcm", // Quickoffice demo |
| "nifkmgcdokhkjghdlgflonppnefddien", // Sheets demo |
| "hdmobeajeoanbanmdlabnbnlopepchip", // Slides demo |
| "ikmidginfdcbojdbmejkeakncgdbmonc", // Soundtrap demo |
| "dgohlccohkojjgkkfholmobjjoledflp", // Spotify demo |
| "dhmdaeekeihmajjnmichlhiffffdbpde", // Store.app demo |
| "onklhlmbpfnmgmelakhgehkfdmkpmekd", // Todoist demo |
| "jeabmjjifhfcejonjjhccaeigpnnjaak", // TweetDeck demo |
| "gnckahkflocidcgjbeheneogeflpjien", // Vine demo |
| "pdckcbpciaaicoomipamcabpdadhofgh", // Weatherbug demo |
| "biliocemfcghhioihldfdmkkhnofcgmb", // Webcam Toy demo |
| "bhfoghflalnnjfcfkaelngenjgjjhapk", // Wevideo demo |
| "pjckdjlmdcofkkkocnmhcbehkiapalho", // Wunderlist demo |
| "pbdihpaifchmclcmkfdgffnnpfbobefh", // YouTube demo |
| |
| // New demo mode: |
| "lpmakjfjcconjeehbidjclhdlpjmfjjj", // Highlights app |
| "iggildboghmjpbjcpmobahnkmoefkike", // Highlights app (eve) |
| "elhbopodaklenjkeihkdhhfaghalllba", // Highlights app (nocturne) |
| "gjeelkjnolfmhphfhhjokaijbicopfln", // Highlights app (other) |
| "mnoijifedipmbjaoekhadjcijipaijjc", // Screensaver |
| "gdobaoeekhiklaljmhladjfdfkigampc", // Screensaver (eve) |
| "lminefdanffajachfahfpmphfkhahcnj", // Screensaver (nocturne) |
| "fafhbhdboeiciklpkminlncemohljlkj", // Screensaver (kukui) |
| "bnabjkecnachpogjlfilfcnlpcmacglh", // Screensaver (other) |
| |
| // Testing extensions: |
| "ongnjlefhnoajpbodoldndkbkdgfomlp", // Show Managed Storage |
| "ilnpadgckeacioehlommkaafedibdeob", // Enterprise DeviceAttributes |
| "oflckobdemeldmjddmlbaiaookhhcngo", // Citrix Receiver QA version |
| "behllobkkfkfnphdnhnkndlbkcpglgmj", // Autotest |
| |
| // Google Apps: |
| "mclkkofklkfljcocdinagocijmpgbhab", // Google input tools |
| "gbkeegbaiigmenfmjfclcdgdpimamgkj", // Office Editing Docs/Sheets/Slides |
| "aapbdbdomjkkjkaonfhkkikfgjllcleb", // Google Translate |
| "mgijmajocgfcbeboacabfgobmjgjcoja", // Google Dictionary |
| "mfhehppjhmmnlfbbopchdfldgimhfhfk", // Google Classroom |
| "mkaakpdehdafacodkgkpghoibnmamcme", // Google Drawings |
| "pnhechapfaindjhompbnflcldabbghjo", // Secure Shell |
| "fcgckldmmjdbpdejkclmfnnnehhocbfp", // Google Finance |
| "jhknlonaankphkkbnmjdlpehkinifeeg", // Google Forms |
| "jndclpdbaamdhonoechobihbbiimdgai", // Chromebook Recovery Utility |
| "aohghmighlieiainnegkcijnfilokake", // Google Docs |
| "eemlkeanncmjljgehlbplemhmdmalhdc", // Chrome Connectivity Diagnostics |
| "eoieeedlomnegifmaghhjnghhmcldobl", // Google Apps Script |
| "ndjpildffkeodjdaeebdhnncfhopkajk", // Network File Share for Chrome OS |
| "pfoeakahkgllhkommkfeehmkfcloagkl", // Fusion Tables |
| "aapocclcgogkmnckokdopfmhonfmgoek", // Google Slides |
| "khpfeaanjngmcnplbdlpegiifgpfgdco", // Smart Card Connector |
| "hmjkmjkepdijhoojdojkdfohbdgmmhki", // Google Keep - notes and lists |
| "felcaaldnbdncclmgdcncolpebgiejap", // Google Sheets |
| "khkjfddibboofomnlkndfedpoccieiee", // Study Kit |
| "becloognjehhioodmnimnehjcibkloed", // Coding with Chrome |
| "hfhhnacclhffhdffklopdkcgdhifgngh", // Camera |
| "adokjfanaflbkibffcbhihgihpgijcei", // Share to Classroom |
| "heildphpnddilhkemkielfhnkaagiabh", // Legacy Browser Support |
| "lpcaedmchfhocbbapmcbpinfpgnhiddi", // Google Keep Chrome Extension |
| "ldipcbpaocekfooobnbcddclnhejkcpn", // Google Scholar Button |
| "nnckehldicaciogcbchegobnafnjkcne", // Google Tone |
| "pfmgfdlgomnbgkofeojodiodmgpgmkac", // Data Saver |
| "djcfdncoelnlbldjfhinnjlhdjlikmph", // High Contrast |
| "ipkjmjaledkapilfdigkgfmpekpfnkih", // Color Enhancer |
| "kcnhkahnjcbndmmehfkdnkjomaanaooo", // Google Voice |
| "nlbjncdgjeocebhnmkbbbdekmmmcbfjd", // RSS Subscription Extension |
| "aoggjnmghgmcllfenalipjhmooomfdce", // SAML SSO for Chrome Apps |
| "fhndealchbngfhdoncgcokameljahhog", // Certificate Enrollment for Chrome OS |
| "npeicpdbkakmehahjeeohfdhnlpdklia", // WebRTC Network Limiter |
| "hdkoikmfpncabbdniojdddokkomafcci", // SSRS Reporting Fix for Chrome |
| }); |
| |
| } // namespace |
| |
| bool IsAllowlistedForManagedGuestSession(const std::string& extension_id) { |
| return kManagedGuestSessionAllowlist.contains(extension_id); |
| } |
| |
| ExtensionsPermissionsTracker::ExtensionsPermissionsTracker( |
| ExtensionRegistry* registry, |
| content::BrowserContext* browser_context) |
| : registry_(registry), |
| pref_service_(Profile::FromBrowserContext(browser_context)->GetPrefs()) { |
| observation_.Observe(registry_.get()); |
| pref_change_registrar_.Init(pref_service_); |
| pref_change_registrar_.Add( |
| pref_names::kInstallForceList, |
| base::BindRepeating( |
| &ExtensionsPermissionsTracker::OnForcedExtensionsPrefChanged, |
| base::Unretained( |
| this))); // Safe as ExtensionsPermissionsTracker |
| // owns pref_change_registrar_ & outlives it |
| // Try to load list now. |
| OnForcedExtensionsPrefChanged(); |
| } |
| |
| ExtensionsPermissionsTracker::~ExtensionsPermissionsTracker() = default; |
| |
| void ExtensionsPermissionsTracker::OnForcedExtensionsPrefChanged() { |
| // TODO(crbug.com/40103683): handle pref_names::kExtensionManagement with |
| // installation_mode: forced. |
| const base::Value& value = |
| pref_service_->GetValue(pref_names::kInstallForceList); |
| if (!value.is_dict()) { |
| return; |
| } |
| |
| extension_safety_ratings_.clear(); |
| pending_forced_extensions_.clear(); |
| |
| for (const auto entry : value.GetDict()) { |
| const ExtensionId& extension_id = entry.first; |
| // By default the extension permissions are assumed to trigger full warning |
| // (false). When the extension is loaded, if all of its permissions is safe, |
| // it'll be marked safe (true) |
| extension_safety_ratings_.insert(make_pair(extension_id, false)); |
| const Extension* extension = |
| registry_->enabled_extensions().GetByID(extension_id); |
| if (extension) |
| ParseExtensionPermissions(extension); |
| else |
| pending_forced_extensions_.insert(extension_id); |
| } |
| if (pending_forced_extensions_.empty()) |
| UpdateLocalState(); |
| } |
| |
| bool ExtensionsPermissionsTracker::IsSafePerms( |
| const PermissionsData* perms_data) const { |
| const PermissionSet& active_permissions = perms_data->active_permissions(); |
| const APIPermissionSet& api_permissions = active_permissions.apis(); |
| for (auto* permission : api_permissions) { |
| if (permission->info()->requires_managed_session_full_login_warning()) { |
| return false; |
| } |
| } |
| const ManifestPermissionSet& manifest_permissions = |
| active_permissions.manifest_permissions(); |
| for (const auto* permission : manifest_permissions) { |
| if (permission->RequiresManagedSessionFullLoginWarning()) { |
| return false; |
| } |
| } |
| if (active_permissions.ShouldWarnAllHosts() || |
| !active_permissions.effective_hosts().is_empty()) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void ExtensionsPermissionsTracker::OnExtensionLoaded( |
| content::BrowserContext* browser_context, |
| const Extension* extension) { |
| auto itr = extension_safety_ratings_.find(extension->id()); |
| if (itr == extension_safety_ratings_.end()) |
| return; |
| pending_forced_extensions_.erase(extension->id()); |
| |
| ParseExtensionPermissions(extension); |
| |
| // If the extension isn't safe or all extensions are loaded, update the local |
| // state. |
| if (!itr->second || pending_forced_extensions_.empty()) |
| UpdateLocalState(); |
| } |
| |
| void ExtensionsPermissionsTracker::UpdateLocalState() { |
| bool any_unsafe = std::ranges::any_of( |
| extension_safety_ratings_, |
| [](const auto& key_value) { return !key_value.second; }); |
| |
| DCHECK(pending_forced_extensions_.empty() || any_unsafe); |
| |
| g_browser_process->local_state()->SetBoolean( |
| prefs::kManagedSessionUseFullLoginWarning, any_unsafe); |
| } |
| |
| // static |
| void ExtensionsPermissionsTracker::RegisterLocalStatePrefs( |
| PrefRegistrySimple* registry) { |
| registry->RegisterBooleanPref(prefs::kManagedSessionUseFullLoginWarning, |
| true); |
| } |
| |
| void ExtensionsPermissionsTracker::ParseExtensionPermissions( |
| const Extension* extension) { |
| extension_safety_ratings_[extension->id()] = |
| IsAllowlistedForManagedGuestSession(extension->id()) || |
| IsSafePerms(extension->permissions_data()); |
| } |
| |
| } // namespace extensions |