-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Never use an http REST API endpoint for https self-hosted sites #25914
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
crazytonyli
merged 2 commits into
release/27.2
from
fix/self-hosted-rest-api-http-downgrade
Aug 20, 2026
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
28 changes: 28 additions & 0 deletions
28
Modules/Sources/WordPressData/Swift/Blog+SelfHostedRestApi.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| import Foundation | ||
|
|
||
| extension Blog { | ||
|
|
||
| /// The WordPress REST API root URL to use for self-hosted network requests. | ||
| /// | ||
| /// Prefers `restApiRootURL` — the root observed during REST API discovery, which is | ||
| /// served over https for an https site — over a URL derived from the raw `xmlrpc` | ||
| /// string. An older app version could silently downgrade and persist an http `xmlrpc` | ||
| /// endpoint for an https site (GHSA-qxpr-7v78-mh5g), and `url(withPath:)` copies that | ||
| /// scheme verbatim, so a REST client built from it would carry the application password | ||
| /// (sent as a Basic auth header) over plaintext http. | ||
| /// | ||
| /// `restApiRootURL` is written together with the application token (see | ||
| /// `ApplicationPasswordRepository.assign` and `Blog.createRestApiBlog`), so it is always | ||
| /// present when the token is. That makes this a discovery-backed choice rather than a | ||
| /// scheme-rewriting guess: an intentionally-http site is left on http (its discovered | ||
| /// root is http), and an https site uses the https root that discovery observed. | ||
| /// | ||
| /// Falls back to the `xmlrpc`-derived `wp-json/` URL only when no discovered root was | ||
| /// persisted (legacy XML-RPC sign-ins), leaving behavior unchanged for those sites. | ||
| public var selfHostedRestApiRootURL: URL? { | ||
| if let restApiRootURL, let url = URL(string: restApiRootURL) { | ||
| return url | ||
| } | ||
| return url(withPath: "wp-json/").flatMap { URL(string: $0) } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
44 changes: 44 additions & 0 deletions
44
Modules/Tests/WordPressDataTests/BlogSelfHostedRestApiTests.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| import CoreData | ||
| import Testing | ||
| @testable import WordPressData | ||
|
|
||
| @MainActor | ||
| struct BlogSelfHostedRestApiTests { | ||
| private let contextManager = ContextManager.forTesting() | ||
|
|
||
| private func makeBlog(url: String?, xmlrpc: String?, restApiRootURL: String?) -> Blog { | ||
| let blog = BlogBuilder(contextManager.mainContext, dotComID: nil).build() | ||
| blog.account = nil | ||
| blog.url = url | ||
| blog.xmlrpc = xmlrpc | ||
| blog.restApiRootURL = restApiRootURL | ||
| return blog | ||
| } | ||
|
|
||
| @Test func prefersDiscoveredHTTPSRootOverDowngradedXMLRPCEndpoint() { | ||
| // An older app version could persist an http xmlrpc endpoint for an https site; the | ||
| // https REST root observed during discovery must win so the application password is | ||
| // never sent over plaintext http. | ||
| let blog = makeBlog( | ||
| url: "https://example.com", | ||
| xmlrpc: "http://example.com/xmlrpc.php", | ||
| restApiRootURL: "https://example.com/wp-json/" | ||
| ) | ||
| #expect(blog.selfHostedRestApiRootURL?.absoluteString == "https://example.com/wp-json/") | ||
| } | ||
|
|
||
| @Test func fallsBackToXMLRPCDerivedRootWhenNoDiscoveredRoot() { | ||
| // Legacy XML-RPC sign-ins never persisted a REST root, so behavior is unchanged. | ||
| let blog = makeBlog( | ||
| url: "https://example.com", | ||
| xmlrpc: "https://example.com/xmlrpc.php", | ||
| restApiRootURL: nil | ||
| ) | ||
| #expect(blog.selfHostedRestApiRootURL?.absoluteString == "https://example.com/wp-json/") | ||
| } | ||
|
|
||
| @Test func returnsNilWhenNeitherRootIsAvailable() { | ||
| let blog = makeBlog(url: "https://example.com", xmlrpc: nil, restApiRootURL: nil) | ||
| #expect(blog.selfHostedRestApiRootURL == nil) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking if
restApiRootURLshould do the same scheme check inxmlrpcURL. But it's probably fine to leave it. The value comes from the api discovery process, and I plan to warn about using http urls in #25870.