Skip to content

fix(remote): refuse a redirect that drops TLS - #3091

Open
vmaerten wants to merge 8 commits into
mainfrom
fix/remote-redirect-tls-downgrade
Open

vmaerten wants to merge 8 commits into
mainfrom
fix/remote-redirect-tls-downgrade

Conversation

@vmaerten

@vmaerten vmaerten commented Nov 5, 2026 •

Copy link
Copy Markdown
Member

Description

An https remote Taskfile redirected to http was downloaded over the plaintext hop without --insecure, and remote.headers were sent on a same-host https to http redirect because they were scoped by host only. Such a redirect now requires --insecure, like an http entrypoint, and configured headers are only sent when both the host and the scheme match the origin. Supersedes #3003.

Test plan

  • TestCheckRedirect*: an https to http hop is refused unless --insecure is set, and the 10-redirect cap still applies.
  • TestBuildHTTPClientRefusesDowngrade / TestBuildHTTPClientFollowsDowngradeWithInsecure: end to end against a TLS server redirecting to a plain one; the plaintext server is never contacted without --insecure.
  • TestHeadersTransport: headers are not added to a request on the same host over http.
  • Manual: run a remote Taskfile behind a real redirecting server, with and without --insecure.

Checklist

  • I have read and followed the Contribution Guide.
  • I have disclosed the use of any AI-generated content in this pull request per the AI Usage Policy.
  • I fully understand the changes and have hand-written the description (No AI) of this pull request.

The scheme was only checked on the URL the user wrote. A server answering
an https URL with a redirect to http was followed by the client without
any further check, so both the HEAD probe and the download travelled in
the clear, and a network attacker could substitute the Taskfile that is
about to be executed.

CheckRedirect now refuses an https to http hop. --insecure does not
loosen it: requesting an http entrypoint is the user's decision, being
sent to one is the server's. Setting CheckRedirect also replaces Go's
default cap, so the ten-hop limit is kept explicitly.

The three call sites turned almost every client error into a generic
download failure, which would have hidden the reason; TaskfileNotSecureError
is now passed through, with wording of its own for the redirect case since
--insecure is not a way out of it.
The same policy — keep TaskfileNotSecureError, make everything else a
generic download failure — was spelled out at each of the three call
sites, where copies of a security rule tend to drift apart.
Refusing it unconditionally was security theatre: --insecure also sets
InsecureSkipVerify, so an attacker in position to intercept can already
serve anything over the https leg with a self-signed certificate. It also
broke an internal server that redirects and works today.

--insecure now means one thing everywhere: the transport guarantees are
waived.
An https remote redirected to http on the same host kept its Host string,
so remote.headers were sent over the plaintext hop. This was reachable
under --insecure, which allows the downgrade redirect.
@vmaerten
vmaerten marked this pull request as ready for review November 5, 2026 20:38

This branch has not been deployed

No deployments
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.

1 participant