CVE-2022-30115: HSTS bypass via trailing dot
What the report claims
A trailing dot in a hostname bypasses HSTS enforcement, allowing HTTP URLs to be treated as HTTPS and causing a downgrade to HTTP after a redirect.
Compare with curl's recorded outcome
Known vulnerability
A real vulnerability: CVE-2022-30115 HSTS bypass via trailing dot
Shown for comparison after analysis. The pipeline did not see this label.
ProveIt's finding
The available evidence does not settle the report. A maintainer should investigate.
Why this finding?
- The provided curl source (lib/urldata.h) contains only type definitions and does not include the HSTS implementation (cache loading, hostname lookup, protocol upgrade logic).
- Deterministic checks returned 'nothing_to_check', meaning the automated grounding could not locate the relevant HSTS code in the revision (0d015fb3f6f4, master on 2022-05-03).
- The trailing-dot bypass is a known vulnerability class in other HTTP clients (Chromium, Mozilla, Wget per external sources E1, E3), making the report plausible, but curl's specific code is absent from the evidence.
- Without the HSTS cache lookup and hostname comparison code, none of the claims can be verified or contradicted.
What still needs checking
- The HSTS implementation files (e.g., lib/hsts.c, lib/hsts.h, or wherever CURLOPT_HSTS is handled and the cache is consulted).
- The hostname normalization or comparison function used during HSTS cache lookup.
- Confirmation of whether HSTS support existed in curl at the target revision (2022-05-03).
How this analysis ran 4 claims · 0 citations verified
Checked against curl master on 2022-05-03 (0d015fb3f6f4). Test evaluation split. Citation verification checks that a referenced line was in the evidence; it does not prove the model interpreted it correctly.
Review a draft reply to the reporter
Edit and verify before sending.
Thank you for the report. The provided code snippets do not include curl's HSTS implementation (cache loading, hostname lookup, or protocol upgrade logic), so we cannot verify the trailing-dot bypass claim. Could you indicate which source files implement CURLOPT_HSTS and the HSTS cache check? If you have a patch or know the function names, that would help us triage quickly.
Inspect all claims and source code 4 claims · 1 code excerpts
Report claims
A trailing dot in the hostname bypasses HSTS check
Read the reasoning
The claim that a trailing dot bypasses the HSTS check requires examining the HSTS cache lookup and hostname comparison logic. The provided source code only shows lib/urldata.h (type definitions), which does not contain the HSTS implementation. The deterministic checks returned 'nothing_to_check', indicating the relevant HSTS code was not found or not provided. Without the HSTS lookup code (likely in a file like lib/hsts.c or similar), this claim cannot be verified or contradicted.
Using a URL with a trailing dot after loading HSTS cache from hsts.txt
Read the reasoning
The trigger describes using a URL with a trailing dot after loading an HSTS cache. This requires the URL parsing code, the --hsts option handling, and the HSTS cache loading logic. None of this code is present in the provided source snippet (lib/urldata.h lines 122-199). The deterministic checks found nothing to check, so the code path cannot be evaluated.
The request is sent over HTTP instead of HTTPS after a redirect
Read the reasoning
The impact claim (request sent over HTTP instead of HTTPS after a redirect) depends on the protocol upgrade logic that consults the HSTS cache. This code is not in the provided source. The external sources (E1-E6) discuss trailing-dot fixes in Wget and browser HSTS preload lists, but do not describe curl's implementation. Without curl's HSTS enforcement code, the claim cannot be decided.
curl --hsts hsts.txt http://accounts.google.com.
Read the reasoning
The reproduction command references CURLOPT_HSTS and CURLOPT_URL option handling and the HSTS cache file format. The provided source does not include the option parsing for --hsts, the HSTS file parser, or the hostname normalization (if any) applied before cache lookup. The deterministic checks found no relevant code to ground this reproduction.
Read the original report
Source code
| 122 | typedef CURLcode (*Curl_datastream)(struct Curl_easy *data, |
| 123 | struct connectdata *conn, |
| 124 | int *didwhat, |
| 125 | bool *done, |
| 126 | int select_res); |
| 127 | #endif |
| 128 | |
| 129 | #include "mime.h" |
| 130 | #include "imap.h" |
| 131 | #include "pop3.h" |
| 132 | #include "smtp.h" |
| 133 | #include "ftp.h" |
| 134 | #include "file.h" |
| 135 | #include "vssh/ssh.h" |
| 136 | #include "http.h" |
| 137 | #include "rtsp.h" |
| 138 | #include "smb.h" |
| 139 | #include "mqtt.h" |
| 140 | #include "wildcard.h" |
| 141 | #include "multihandle.h" |
| 142 | #include "quic.h" |
| 143 | #include "c-hyper.h" |
| 144 | |
| 145 | #ifdef HAVE_GSSAPI |
| 146 | # ifdef HAVE_GSSGNU |
| 147 | # include <gss.h> |
| 148 | # elif defined HAVE_GSSAPI_GSSAPI_H |
| 149 | # include <gssapi/gssapi.h> |
| 150 | # else |
| 151 | # include <gssapi.h> |
| 152 | # endif |
| 153 | # ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H |
| 154 | # include <gssapi/gssapi_generic.h> |
| 155 | # endif |
| 156 | #endif |
| 157 | |
| 158 | #ifdef HAVE_LIBSSH2_H |
| 159 | #include <libssh2.h> |
| 160 | #include <libssh2_sftp.h> |
| 161 | #endif /* HAVE_LIBSSH2_H */ |
| 162 | |
| 163 | #define READBUFFER_SIZE CURL_MAX_WRITE_SIZE |
| 164 | #define READBUFFER_MAX CURL_MAX_READ_SIZE |
| 165 | #define READBUFFER_MIN 1024 |
| 166 | |
| 167 | /* The default upload buffer size, should not be smaller than |
| 168 | CURL_MAX_WRITE_SIZE, as it needs to hold a full buffer as could be sent in |
| 169 | a write callback. |
| 170 | |
| 171 | The size was 16KB for many years but was bumped to 64KB because it makes |
| 172 | libcurl able to do significantly faster uploads in some circumstances. Even |
| 173 | larger buffers can help further, but this is deemed a fair memory/speed |
| 174 | compromise. */ |
| 175 | #define UPLOADBUFFER_DEFAULT 65536 |
| 176 | #define UPLOADBUFFER_MAX (2*1024*1024) |
| 177 | #define UPLOADBUFFER_MIN CURL_MAX_WRITE_SIZE |
| 178 | |
| 179 | #define CURLEASY_MAGIC_NUMBER 0xc0dedbadU |
| 180 | #define GOOD_EASY_HANDLE(x) \ |
| 181 | ((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) |
| 182 | |
| 183 | /* the type we use for storing a single boolean bit */ |
| 184 | #ifdef _MSC_VER |
| 185 | typedef bool bit; |
| 186 | #define BIT(x) bool x |
| 187 | #else |
| 188 | typedef unsigned int bit; |
| 189 | #define BIT(x) bit x:1 |
| 190 | #endif |
| 191 | |
| 192 | #ifdef HAVE_GSSAPI |
| 193 | /* Types needed for krb5-ftp connections */ |
| 194 | struct krb5buffer { |
| 195 | void *data; |
| 196 | size_t size; |
| 197 | size_t index; |
| 198 | BIT(eof_flag); |
| 199 | }; |