Skip to content

Create a confirmation dialog to update root certificates when downloading add-ons#18402

Merged
SaschaCowley merged 20 commits into
betafrom
checkRootCertForAddonDL
Jul 15, 2025
Merged

Create a confirmation dialog to update root certificates when downloading add-ons#18402
SaschaCowley merged 20 commits into
betafrom
checkRootCertForAddonDL

Conversation

@seanbudd

@seanbudd seanbudd commented Jul 3, 2025

Copy link
Copy Markdown
Member

Link to issue number:

Related #15905 #18354

Summary of the issue:

When trying to download an add-on from the add-on store in certain environments such as corporates, the download fails due to the machine not trusting the root certificate of the add-on source.

This can be caused by two issues:

  1. the requests library using certifi rather than the system root certificates
  2. the system root certificates not trusting the add-on download source

Additionally #18354 changed the NVDA update check to use requests rather than urllib, which would have introduced the issue with certifi to the update check instead of just the add-on store.

Description of user facing changes:

  • Use system root certificates rather than certifi, preventing issues accessing the add-on store or update check in corporate environments
  • Add a dialog to give the user an option for trusting root certificates for add-on sources, such as github, if the system doesn't trust the download source

Description of developer facing changes:

None

Description of development approach:

  • add pip_system_certs which overrides certifi to use system certs for requests
  • use our existing code to update system certs when downloading add-ons
  • ignore the patch when wrapping the socket for remote. This is due to an unexpected bug with the patch. This should be reconsidered when updating python, pip_system_certs, and remote to e2ee
  • Note: PROTOCOL_TLSv1_2 is deprecated. Fix up usage to match new python suggested code

Testing strategy:

  • Manual testing from users
  • Smoke tested remote connection

Known issues with pull request:

None

Code Review Checklist:

  • Documentation:
    • Change log entry
    • User Documentation
    • Developer / Technical Documentation
    • Context sensitive help for GUI changes
  • Testing:
    • Unit tests
    • System (end to end) tests
    • Manual testing
  • UX of all users considered:
    • Speech
    • Braille
    • Low Vision
    • Different web browsers
    • Localization in other languages / culture than English
  • API is compatible with existing add-ons.
  • Security precautions taken.

@coderabbitai summary

Copilot AI review requested due to automatic review settings July 3, 2025 02:19
@seanbudd seanbudd requested a review from a team as a code owner July 3, 2025 02:19
@seanbudd seanbudd requested a review from SaschaCowley July 3, 2025 02:19

This comment was marked as outdated.

@AppVeyorBot

This comment was marked as resolved.

@hwf1324

This comment was marked as resolved.

Comment thread source/addonStore/network.py Outdated
@hwf1324

hwf1324 commented Jul 3, 2025

Copy link
Copy Markdown
Contributor

This is useless; it will only prompt me to confirm the trust certificate over and over again.

As I mentioned in #15905 (comment), Requests uses certificates from the independent certifi package. This requires adjusting the requests.get call.

IO - speech.speech.speak (11:52:12.910) - MainThread (28088):
Speaking [LangChangeCommand ('zh_CN'), 'Log fragment start position marked, press again to copy to clipboard']
IO - inputCore.InputManager.executeGesture (11:52:14.728) - winInputHook (27932):
Input: kb(laptop):enter
IO - speech.speech.speak (11:52:14.757) - MainThread (28088):
Speaking [LangChangeCommand ('zh_CN'), '上下文', 'menu', CancellableSpeech (still valid)]
IO - inputCore.InputManager.executeGesture (11:52:15.680) - winInputHook (27932):
Input: kb(laptop):downArrow
IO - speech.speech.speak (11:52:15.697) - MainThread (28088):
Speaking [LangChangeCommand ('zh_CN'), 'Install', CharacterModeCommand(True), 'i', CharacterModeCommand(False), CancellableSpeech (still valid)]
IO - inputCore.InputManager.executeGesture (11:52:16.748) - winInputHook (27932):
Input: kb(laptop):enter
DEBUG - gui.addonStoreGui.controls.actions._MonoActionsContextMenu._menuItemClicked (11:52:16.766) - MainThread (28088):
action selected: actionVM: &Install, selectedAddon: AddonListItemVM: Access8Math-Channel.STABLE, AvailableAddonStatus.AVAILABLE
DEBUG - gui.addonStoreGui.viewModels.addonList.AddonListItemVM.status (11:52:16.766) - MainThread (28088):
addon status change: Access8Math-Channel.STABLE: status: AvailableAddonStatus.DOWNLOADING
DEBUG - gui.addonStoreGui.viewModels.store.AddonStoreVM.getAddon (11:52:16.766) - MainThread (28088):
Access8Math-Channel.STABLE status: AvailableAddonStatus.DOWNLOADING
DEBUG - addonStore.network.AddonFileDownloader._download (11:52:16.767) - AddonDownloader_0 (47400):
starting download: Access8Math
DEBUG - gui.addonStoreGui.controls.addonList.AddonVirtualList.OnItemActivated (11:52:16.769) - MainThread (28088):
item activated: 0
DEBUGWARNING - Python warning (11:52:16.775) - AddonDownloader_0 (47400):
gui\message.pyc:130: DeprecationWarning: gui.message.messageBox is deprecated. Use gui.message.MessageDialog instead.
DEBUG - gui.contextHelp.bindHelpEvent (11:52:16.794) - MainThread (28088):
Did context help binding for MessageDialog
DEBUG - gui.message.MessageDialog.ShowModal (11:52:16.826) - MainThread (28088):
Adding <gui.message.MessageDialog object at 0x16971DA8> to instances.
DEBUG - gui.message.MessageDialog.ShowModal (11:52:16.826) - MainThread (28088):
Showing <gui.message.MessageDialog object at 0x16971DA8> as modal
DEBUG - gui.addonStoreGui.viewModels.addonList.AddonListVM._itemDataUpdated (11:52:16.860) - MainThread (28088):
Item updated: AddonListItemVM: Access8Math-Channel.STABLE, AvailableAddonStatus.DOWNLOADING
DEBUG - gui.addonStoreGui.viewModels.addonList.AddonListVM._itemDataUpdated (11:52:16.860) - MainThread (28088):
Notifying of update
IO - speech.speech.speak (11:52:16.876) - MainThread (28088):
Speaking [LangChangeCommand ('zh_CN'), 'Add-on download failure', 'dialog', 'The website where you are downloading the add-on from has a\ncertificate that is not trusted. Do you want to trust the root\ncertificate for github.com? This will allow you to download add-ons\nfrom this website in the future. Only do this if you trust the website. ', CancellableSpeech (still valid)]
IO - speech.speech.speak (11:52:16.879) - MainThread (28088):
Speaking [LangChangeCommand ('zh_CN'), 'OK', 'button', CancellableSpeech (still valid)]
DEBUG - gui.addonStoreGui.controls.addonList.AddonVirtualList._itemDataUpdated (11:52:16.908) - MainThread (28088):
index: 0
IO - inputCore.InputManager.executeGesture (11:52:17.857) - winInputHook (27932):
Input: kb(laptop):enter
DEBUG - gui.message.MessageDialog._onButtonEvent (11:52:17.861) - MainThread (28088):
Got button event on id=5100
DEBUG - gui.message.MessageDialog._onCloseEvent (11:52:17.879) - MainThread (28088):
Queueing <gui.message.MessageDialog object at 0x16971DA8> for destruction
DEBUG - gui.message.MessageDialog._onCloseEvent (11:52:17.879) - MainThread (28088):
Removing <gui.message.MessageDialog object at 0x16971DA8> from instances.
DEBUG - utils.networking._updateWindowsRootCertificates (11:52:17.879) - AddonDownloader_0 (47400):
Updating Windows root certificates
DEBUGWARNING - Python warning (11:52:17.913) - AddonDownloader_0 (47400):
urllib3\connectionpool.pyc:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host 'github.com'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
IO - speech.speech.speak (11:52:17.976) - MainThread (28088):
Speaking [LangChangeCommand ('zh_CN'), 'Add-on Store - Available add-ons (Stable)', 'dialog', 'Access8Math', CancellableSpeech (still valid)]
IO - speech.speech.speak (11:52:17.984) - MainThread (28088):
Speaking [LangChangeCommand ('zh_CN'), 'Available add-ons:', 'list', 'Alt+', CharacterModeCommand(True), 'a', CharacterModeCommand(False), CancellableSpeech (still valid)]
IO - speech.speech.speak (11:52:18.022) - MainThread (28088):
Speaking [LangChangeCommand ('zh_CN'), 'Access8Math; Status: Downloading; Available version: 4.3; Channel: Stable; Publisher: Tseng Woody; Date: 6/12/2025', '1 of 135', CancellableSpeech (still valid)]
DEBUGWARNING - Python warning (11:52:18.561) - AddonDownloader_0 (47400):
urllib3\connectionpool.pyc:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host 'objects.githubusercontent.com'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
DEBUG - addonStore.network.AddonFileDownloader._download (11:52:20.081) - AddonDownloader_0 (47400):
starting download: Access8Math
DEBUG - gui.contextHelp.bindHelpEvent (11:52:20.327) - MainThread (28088):
Did context help binding for MessageDialog
DEBUG - gui.message.MessageDialog.ShowModal (11:52:20.336) - MainThread (28088):
Adding <gui.message.MessageDialog object at 0x1697FD60> to instances.
DEBUG - gui.message.MessageDialog.ShowModal (11:52:20.336) - MainThread (28088):
Showing <gui.message.MessageDialog object at 0x1697FD60> as modal
IO - speech.speech.speak (11:52:20.387) - MainThread (28088):
Speaking [LangChangeCommand ('zh_CN'), 'Add-on download failure', 'dialog', 'The website where you are downloading the add-on from has a\ncertificate that is not trusted. Do you want to trust the root\ncertificate for github.com? This will allow you to download add-ons\nfrom this website in the future. Only do this if you trust the website. ', CancellableSpeech (still valid)]
IO - speech.speech.speak (11:52:20.391) - MainThread (28088):
Speaking [LangChangeCommand ('zh_CN'), 'OK', 'button', CancellableSpeech (still valid)]
IO - inputCore.InputManager.executeGesture (11:52:21.394) - winInputHook (27932):
Input: kb(laptop):enter
DEBUG - gui.message.MessageDialog._onButtonEvent (11:52:21.402) - MainThread (28088):
Got button event on id=5100
DEBUG - gui.message.MessageDialog._onCloseEvent (11:52:21.414) - MainThread (28088):
Queueing <gui.message.MessageDialog object at 0x1697FD60> for destruction
DEBUG - gui.message.MessageDialog._onCloseEvent (11:52:21.414) - MainThread (28088):
Removing <gui.message.MessageDialog object at 0x1697FD60> from instances.
DEBUG - utils.networking._updateWindowsRootCertificates (11:52:21.414) - AddonDownloader_0 (47400):
Updating Windows root certificates
DEBUGWARNING - Python warning (11:52:21.442) - AddonDownloader_0 (47400):
urllib3\connectionpool.pyc:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host 'github.com'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
IO - speech.speech.speak (11:52:21.507) - MainThread (28088):
Speaking [LangChangeCommand ('zh_CN'), 'Add-on Store - Available add-ons (Stable)', 'dialog', 'Access8Math', CancellableSpeech (still valid)]
IO - speech.speech.speak (11:52:21.516) - MainThread (28088):
Speaking [LangChangeCommand ('zh_CN'), 'Available add-ons:', 'list', 'Alt+', CharacterModeCommand(True), 'a', CharacterModeCommand(False), CancellableSpeech (still valid)]
DEBUGWARNING - Python warning (11:52:21.568) - AddonDownloader_0 (47400):
urllib3\connectionpool.pyc:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host 'objects.githubusercontent.com'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
IO - speech.speech.speak (11:52:21.583) - MainThread (28088):
Speaking [LangChangeCommand ('zh_CN'), 'Access8Math; Status: Downloading; Available version: 4.3; Channel: Stable; Publisher: Tseng Woody; Date: 6/12/2025', '1 of 135', CancellableSpeech (still valid)]
DEBUG - addonStore.network.AddonFileDownloader._download (11:52:22.816) - AddonDownloader_0 (47400):
starting download: Access8Math
DEBUG - gui.contextHelp.bindHelpEvent (11:52:22.854) - MainThread (28088):
Did context help binding for MessageDialog
DEBUG - gui.message.MessageDialog.ShowModal (11:52:22.864) - MainThread (28088):
Adding <gui.message.MessageDialog object at 0x1697F220> to instances.
DEBUG - gui.message.MessageDialog.ShowModal (11:52:22.864) - MainThread (28088):
Showing <gui.message.MessageDialog object at 0x1697F220> as modal
IO - speech.speech.speak (11:52:22.914) - MainThread (28088):
Speaking [LangChangeCommand ('zh_CN'), 'Add-on download failure', 'dialog', 'The website where you are downloading the add-on from has a\ncertificate that is not trusted. Do you want to trust the root\ncertificate for github.com? This will allow you to download add-ons\nfrom this website in the future. Only do this if you trust the website. ', CancellableSpeech (still valid)]
IO - speech.speech.speak (11:52:22.918) - MainThread (28088):
Speaking [LangChangeCommand ('zh_CN'), 'OK', 'button', CancellableSpeech (still valid)]
IO - inputCore.InputManager.executeGesture (11:52:23.833) - winInputHook (27932):
Input: kb(laptop):enter
DEBUG - gui.message.MessageDialog._onButtonEvent (11:52:23.837) - MainThread (28088):
Got button event on id=5100
DEBUG - gui.message.MessageDialog._onCloseEvent (11:52:23.859) - MainThread (28088):
Queueing <gui.message.MessageDialog object at 0x1697F220> for destruction
DEBUG - gui.message.MessageDialog._onCloseEvent (11:52:23.859) - MainThread (28088):
Removing <gui.message.MessageDialog object at 0x1697F220> from instances.
DEBUG - utils.networking._updateWindowsRootCertificates (11:52:23.860) - AddonDownloader_0 (47400):
Updating Windows root certificates
DEBUGWARNING - Python warning (11:52:23.906) - AddonDownloader_0 (47400):
urllib3\connectionpool.pyc:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host 'github.com'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
IO - speech.speech.speak (11:52:23.955) - MainThread (28088):
Speaking [LangChangeCommand ('zh_CN'), 'Add-on Store - Available add-ons (Stable)', 'dialog', 'Access8Math', CancellableSpeech (still valid)]
IO - speech.speech.speak (11:52:23.960) - MainThread (28088):
Speaking [LangChangeCommand ('zh_CN'), 'Available add-ons:', 'list', 'Alt+', CharacterModeCommand(True), 'a', CharacterModeCommand(False), CancellableSpeech (still valid)]
IO - speech.speech.speak (11:52:24.028) - MainThread (28088):
Speaking [LangChangeCommand ('zh_CN'), 'Access8Math; Status: Downloading; Available version: 4.3; Channel: Stable; Publisher: Tseng Woody; Date: 6/12/2025', '1 of 135', CancellableSpeech (still valid)]
DEBUGWARNING - Python warning (11:52:24.030) - AddonDownloader_0 (47400):
urllib3\connectionpool.pyc:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host 'objects.githubusercontent.com'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
DEBUG - addonStore.network.AddonFileDownloader._download (11:52:25.253) - AddonDownloader_0 (47400):
starting download: Access8Math
DEBUG - gui.contextHelp.bindHelpEvent (11:52:25.264) - MainThread (28088):
Did context help binding for MessageDialog
DEBUG - gui.message.MessageDialog.ShowModal (11:52:25.280) - MainThread (28088):
Adding <gui.message.MessageDialog object at 0x1697F4A8> to instances.
DEBUG - gui.message.MessageDialog.ShowModal (11:52:25.281) - MainThread (28088):
Showing <gui.message.MessageDialog object at 0x1697F4A8> as modal
IO - speech.speech.speak (11:52:25.332) - MainThread (28088):
Speaking [LangChangeCommand ('zh_CN'), 'Add-on download failure', 'dialog', 'The website where you are downloading the add-on from has a\ncertificate that is not trusted. Do you want to trust the root\ncertificate for github.com? This will allow you to download add-ons\nfrom this website in the future. Only do this if you trust the website. ', CancellableSpeech (still valid)]
IO - speech.speech.speak (11:52:25.334) - MainThread (28088):
Speaking [LangChangeCommand ('zh_CN'), 'OK', 'button', CancellableSpeech (still valid)]
IO - inputCore.InputManager.executeGesture (11:52:27.712) - winInputHook (27932):
Input: kb(laptop):NVDA+control+shift+f1

@seanbudd

seanbudd commented Jul 3, 2025

Copy link
Copy Markdown
Member Author

Ah I see we need to change the certificate source for all requests made through requests. Thanks for testing

@seanbudd

seanbudd commented Jul 3, 2025

Copy link
Copy Markdown
Member Author

Actually, instead I think we should change our code to update the certifi certs, rather than windows root certificates: e.g. https://gist.github.com/jetersen/5efca1278e153aacc8413da71e77494c

@hwf1324

hwf1324 commented Jul 3, 2025

Copy link
Copy Markdown
Contributor

Although certifi does not provide a way to modify the trust certificate store, we may have no choice but to do so.

It is important to note that certs.pem may also be updated each time NVDA is updated.

@seanbudd

seanbudd commented Jul 3, 2025

Copy link
Copy Markdown
Member Author

@hwf1324 - I've done some testing and I think overriding requests to use system certs is the best option we have https://pypi.org/project/pip-system-certs/

Can you try the next build?

@hwf1324

hwf1324 commented Jul 3, 2025

Copy link
Copy Markdown
Contributor

No changes. Maybe I should run it from the source and observe the specific situation.

@AppVeyorBot

Copy link
Copy Markdown
  • PASS: Translation comments check.
  • FAIL: License check. See C:\projects\nvda\testOutput\license\licenseCheckResults.md for more information.
  • FAIL: Unit tests. See test results for more information.
  • PASS: System tests (tags: installer NVDA).
  • Build (for testing PR): https://ci.appveyor.com/api/buildjobs/khcaajv4pgihxxq9/artifacts/output/nvda_snapshot_pr18402-37137,e99f2feb.exe
  • CI timing (mins):
    INIT 0.0,
    INSTALL_START 1.5,
    INSTALL_END 0.9,
    BUILD_START 0.0,
    BUILD_END 20.3,
    TESTSETUP_START 0.0,
    TESTSETUP_END 0.4,
    TEST_START 0.0,
    TEST_END 20.7,
    FINISH_END 0.1

See test results for failed build of commit e99f2feb6f

@hwf1324

hwf1324 commented Jul 3, 2025

Copy link
Copy Markdown
Contributor

After running it from source, I successfully downloaded the add-on. It may have been caused by an incorrect launcher version downloaded from Actions previously.

@seanbudd

seanbudd commented Jul 3, 2025

Copy link
Copy Markdown
Member Author

@hwf1324 it's possible that the patch is not being bundled correctly in the installer, I'll look into making sure it's exported correctly

@seanbudd

seanbudd commented Jul 4, 2025

Copy link
Copy Markdown
Member Author

@hwf1324 - can you test the launcher from AppVeyor in the latest comment?
https://ci.appveyor.com/api/buildjobs/khcaajv4pgihxxq9/artifacts/output/nvda_snapshot_pr18402-37137,e99f2feb.exe

I tested it, and it appears to be loading the SSL certificates from the right place

@hwf1324

hwf1324 commented Jul 4, 2025

Copy link
Copy Markdown
Contributor

Yes, I can download add-ons directly from GitHub. no confirmation dialog pops up.

So do we still need this dialog box? I think the credentials added through it are unclear.

This comment was marked as outdated.

@seanbudd seanbudd requested a review from hwf1324 July 4, 2025 04:41
@AppVeyorBot

Copy link
Copy Markdown

See test results for failed build of commit 3384603aa5

@seanbudd seanbudd requested a review from Copilot July 10, 2025 05:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR ensures that NVDA uses system root certificates instead of certifi and prompts users to trust unverified root certificates when downloading add-ons.

  • Switches requests to use system certificates by injecting pip_system_certs.
  • Adds a confirmation dialog to allow trusting a site’s root certificate on download failures.
  • Patches remote client socket wrapping to avoid unwanted pip_system_certs behavior.

Reviewed Changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
user_docs/en/changes.md Added changelog entry for the add-on store certificate fix.
source/utils/networking.py Refactored root‐cert update functions and added _getCertificate.
source/core.py Injects pip_system_certs in main() to use system certificates.
source/addonStore/network.py Implements _handleCertVerificationError and prompts user dialog.
source/addonStore/dataManager.py Uses _fetchUrlAndUpdateRootCertificates for add-on store fetch.
source/_remoteClient/transport.py Adds mock patch around wrap_socket to avoid truststore bug.
source/_remoteClient/server.py Applies same mock.patch workaround for server socket.
pyproject.toml Added pip_system_certs dependency and ignored certifi.
Comments suppressed due to low confidence (3)

source/utils/networking.py:44

  • [nitpick] Function name _getCertificate uses camelCase, which is inconsistent with the project's snake_case convention. Rename it to _get_certificate.
def _getCertificate(url: str) -> bytes:

source/addonStore/network.py:316

  • There are no unit tests covering the new certificate-verification fallback and user confirmation dialog flow. Consider adding tests for _handleCertVerificationError and its dialog branch to ensure stability.
		except requests.exceptions.SSLError as e:

source/addonStore/dataManager.py:128

  • No import for _fetchUrlAndUpdateRootCertificates is present in this file, which will cause a NameError at runtime. Add from utils.networking import _fetchUrlAndUpdateRootCertificates at the top.
			response = _fetchUrlAndUpdateRootCertificates(url)

Comment thread source/_remoteClient/transport.py
Comment thread source/_remoteClient/transport.py
Comment thread source/_remoteClient/server.py
Comment thread source/utils/networking.py
Comment thread source/addonStore/network.py Outdated
Comment thread source/addonStore/network.py Outdated
@SaschaCowley SaschaCowley merged commit b9ae452 into beta Jul 15, 2025
15 of 16 checks passed
@SaschaCowley SaschaCowley deleted the checkRootCertForAddonDL branch July 15, 2025 01:35
SaschaCowley pushed a commit that referenced this pull request Jul 18, 2025
Fixes #18460 
Fixup of #18354 

Summary of the issue:
There is a fragile workaround introduced in
#18402 (comment).
This is also a soft API breakage for add-ons, forcing teleNVDA to use
the workaround

This is likely a bug fixed in truststore
sethmlarson/truststore#149

We are using pip_system_certs to patch in an old version of truststore.
this [is no longer being actively
maintained](https://gitlab.com/alelec/pip-system-certs/-/issues/27#note_2372267321)
and apparently truststore has the same patching feature now.

Description of user facing changes:
None

Description of developer facing changes:
Fix workaround which was required for add-ons such as tele NVDA

Testing strategy:
Ensure #18460 is not
reintroduced.
Make sure NVDA can still use update check and the add-on store in
corporate network environments.
seanbudd added a commit that referenced this pull request Jul 22, 2025
seanbudd added a commit that referenced this pull request Jul 22, 2025
### Reverts PR
Reverts #18354
Reverts #18490
Reverts #18402

### Issues fixed
<!-- Issues that will be closed by reverting, i.e. the issues introduced
via the PR getting reverted -->
Fixes #18519 

### Issues reopened
<!-- Issues that will be re-opened by reverting, i.e. the issues that
were fixed via the PR getting reverted -->
Reopens #15905

### Reason for revert
The current implementation breaks NVDA remote and breaks add-on APIs

### Can this PR be reimplemented? If so, what is required for the next
attempt
- use truststore.SSLContext directly rather than source code wide
injections. It is
[unclear](https://truststore.readthedocs.io/en/latest/#using-truststore-with-requests)
how to do that with requests. [this SO answer suggestions an
option](https://stackoverflow.com/a/78265028).
- perform the changes in an API breaking release, encourage authors to
use `inject_into_ssl, extract_from_ssl` where required
@CyrilleB79

Copy link
Copy Markdown
Contributor

@seanbudd, @SaschaCowley, please note that due to this PR, DEBUGWARNING in the log have been transformed into VERBOSE.

I have tested various alpha snapshots. The VERBOSE instead of DEBUGWARNING are included between nvda_snapshot_alpha-37303,e3eba94f and nvda_snapshot_alpha-37400,52f75ef7, both included. Since nvda_snapshot_alpha-37426,d1d0dbdb, DEBUGWARNING is present again in the log, probably due to most recent update or revert on this topic.

Please pay attention to this point when working again on this part of code, if applicable (e.g. in #18528)

@SaschaCowley

Copy link
Copy Markdown
Member

@CyrilleB79 this PR has been reverted. I am also not sure what you mean by "The VERBOSE instead of DEBUGWARNING". Could you give an example?

@CyrilleB79

Copy link
Copy Markdown
Contributor

@SaschaCowley sure, here is an example:

In NVDA 2025.2beta3, in which this PR had not yet been reverted, in the log I get:

VERBOSE - core.main (08:32:32.757) - MainThread (14088):
Slow starting core (12.64 sec)

whereas it should be:

DEBUGWARNING - core.main (08:32:32.757) - MainThread (14088):
Slow starting core (12.64 sec)

I know that this PR has been reverted. But I comment here to document the issue present in this PR so that when reimplementing it (e.g. #18528), you do not reintroduce this issue.

SaschaCowley added a commit that referenced this pull request Oct 10, 2025
…8528)

Fixes #15905
Reimplements #18354, #18402, #18490

### Summary of the issue:
When trying to download an add-on from the add-on store in certain
environments such as corporates, the download fails due to the machine
not trusting the root certificate of the add-on source.

This can be caused by two issues:

* the requests library using certifi rather than the system root
certificates
* the system root certificates not trusting the add-on download source

When performing an update check, we update windows root certificates if
our certificate is invalid or out of date.

### Description of user facing changes:
NVDA should more reliably trust request endpoints using windows root
certificates. e.g. accessing the add-on store in a corporate
environment.

### Description of developer facing changes:
requests and similar libraries now has truststore injected into it

### Description of development approach:
This pull request improves how NVDA handles SSL/TLS certificate
verification, particularly for HTTPS requests and add-on downloads. It
ensures that the application uses the Windows root certificate store
instead of the Python default (certifi), provides a fallback mechanism
to update root certificates when encountering untrusted certificates,
and enhances user interaction when certificate errors occur during
add-on downloads. Additionally, shared networking logic has been
refactored and centralized for maintainability.

**Certificate Verification and Networking Improvements:**

* Added the `truststore` dependency and injected it at startup so that
all `requests` HTTP calls use the Windows root certificate store instead
of certifi, improving compatibility in corporate and managed
environments. (`pyproject.toml`, `source/core.py`)
[[1]](diffhunk://#diff-50c86b7ed8ac2cf95bd48334961bf0530cdc77b5a56f852c5c61b89d735fd711R32-R33)
[[2]](diffhunk://#diff-dbe862167add4fbe8e502757f792615f714c7603621c03d0c60c08a6a613ad68R34)
[[3]](diffhunk://#diff-dbe862167add4fbe8e502757f792615f714c7603621c03d0c60c08a6a613ad68R680-R683)
* Introduced a new `utils/networking.py` module that centralizes logic
for fetching URLs, handling certificate verification errors, and
updating Windows root certificates. This includes helper functions like
`_fetchUrlAndUpdateRootCertificates`, `_getCertificate`, and
`_updateWindowsRootCertificates`. (`source/utils/networking.py`)

**Add-on Store and Update Check Enhancements:**

* Updated add-on store and update check logic to use the new
`_fetchUrlAndUpdateRootCertificates` function, ensuring robust handling
of certificate verification failures and automatic root certificate
updates when necessary. (`source/addonStore/dataManager.py`,
`source/updateCheck.py`)
[[1]](diffhunk://#diff-9fd9bbb610bac3157554c0876fba6ea3475383ea9de36c2101ba1127cf5189e5R30)
[[2]](diffhunk://#diff-9fd9bbb610bac3157554c0876fba6ea3475383ea9de36c2101ba1127cf5189e5L127-R128)
[[3]](diffhunk://#diff-9fd9bbb610bac3157554c0876fba6ea3475383ea9de36c2101ba1127cf5189e5L138-R143)
[[4]](diffhunk://#diff-69aa1f3d46c308cf71c96483fd196eb49d6348727ab1953f834b1974ff031a43R68-R76)
[[5]](diffhunk://#diff-69aa1f3d46c308cf71c96483fd196eb49d6348727ab1953f834b1974ff031a43L241-R245)
* Improved the add-on download process to prompt the user when
encountering an untrusted certificate, displaying the SHA256 fingerprint
and allowing the user to trust and install the root certificate if
appropriate. (`source/addonStore/network.py`)
[[1]](diffhunk://#diff-097e46da59cc87eb0100bd63db7cfe32c7095ec66cf4be02d5dc84f28682ac7eL239-R297)
[[2]](diffhunk://#diff-097e46da59cc87eb0100bd63db7cfe32c7095ec66cf4be02d5dc84f28682ac7eR314-R315)

**Codebase Cleanup and Refactoring:**

* Removed duplicate and now-unnecessary certificate update logic from
`updateCheck.py` and centralized all certificate handling in
`utils/networking.py`. (`source/updateCheck.py`)
* Updated variable naming and improved code clarity in the add-on
download logic. (`source/addonStore/network.py`)
[[1]](diffhunk://#diff-097e46da59cc87eb0100bd63db7cfe32c7095ec66cf4be02d5dc84f28682ac7eL239-R297)
[[2]](diffhunk://#diff-097e46da59cc87eb0100bd63db7cfe32c7095ec66cf4be02d5dc84f28682ac7eR314-R315)
[[3]](diffhunk://#diff-097e46da59cc87eb0100bd63db7cfe32c7095ec66cf4be02d5dc84f28682ac7eL268-R324)
[[4]](diffhunk://#diff-097e46da59cc87eb0100bd63db7cfe32c7095ec66cf4be02d5dc84f28682ac7eL278-R334)
[[5]](diffhunk://#diff-097e46da59cc87eb0100bd63db7cfe32c7095ec66cf4be02d5dc84f28682ac7eL290-R346)

### Testing strategy:

- [x] Get user testing from #15905
- [x] Smoke test using add-on store
- [x] Smoke test update check

### Known issues with pull request:
None

---------

Co-authored-by: Sascha Cowley <16543535+SaschaCowley@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants