Skip to content

Commit 6153e3b

Browse files
authored
VERCEL: Fix some bugs (#3887)
The PR follows #3542 Found some bugs when running intergration tests locally again, and the PR is an attempt to fix them: - When updating/creating HTTPS/SRV records, Vercel API only reads from the corresponding struct (either `srv` or `https`). If we provide a `value`, the Vercel API will reject with an error. - The PR makes `Value` "nil-able", and sets `Value` to nil when dealing with `SRV` or `HTTPS` records. - When updating a record, currently, we treat the empty SVC param as omitting the field. But with Vercel's API, omitting a field means not updating the field. We need to explicitly make the field an empty string to create/update an empty SVC param, and the PR does that. - Vercel implements an unknown `ech=` parameter validation process for HTTPS records. The validation process is unknown, undocumented, thus I can't implement a `rejectif` for `AuditRecord`. - Let's make this a known caveat, describe it in the provider docs, skip these intergration tests, and move on. Please tag this PR w/ `provider-VERCEL`.
1 parent 7dc81bb commit 6153e3b

File tree

4 files changed

+47
-6
lines changed

4 files changed

+47
-6
lines changed

documentation/provider/vercel.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,13 @@ Vercel does not allow the record type to be changed after creation. If you try t
144144
### Minimum TTL
145145

146146
Vercel enforces a minimum TTL of 60 seconds (1 minute) for all records. We will always silently override the TTL to 60 seconds if you try to set a lower TTL.
147+
148+
### HTTPS Record ECH Base64 Validation
149+
150+
Currently, Vercel does implements IETF's "Bootstrapping TLS Encrypted ClientHello with DNS Service Bindings" draft. However, Vercel also implements a validation process for the `ech` parameter in the `HTTPS` records, and will reject the request with the following error message if Vercel considers the `ech` value is invalid:
151+
152+
```
153+
Invalid base64 string: [input] (key: ech)
154+
```
155+
156+
The detail of Vercel's validation process is unknown, thus we can not support static validation for `dnscontrol check` or `dnscontrol preview`. You should use `ech=` with caution.

integrationTest/integration_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,18 @@ func makeTests() []*TestGroup {
292292

293293
testgroup("Ech",
294294
requires(providers.CanUseHTTPS),
295+
not(
296+
// Last tested in 2025-12-04. Turns out that Vercel implements an unknown validation
297+
// on the `ech` parameter, and our dummy base64 string are being rejected with:
298+
//
299+
// Invalid base64 string: [our base64] (key: ech)
300+
//
301+
// Since Vercel's validation process is unknown and not documented, we can't implement
302+
// a rejectif within auditrecord to reject them statically.
303+
//
304+
// Let's just ignore ECH test for Vercel for now.
305+
"VERCEL",
306+
),
295307
tc("Create a HTTPS record", https("@", 1, "example.com.", "alpn=h2,h3")),
296308
tc("Add an ECH key", https("@", 1, "example.com.", "alpn=h2,h3 ech=some+base64+encoded+value///")),
297309
tc("Ignore the ECH key while changing other values", https("@", 1, "example.net.", "port=80 ech=IGNORE")),

providers/vercel/api.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,13 @@ func (c *vercelProvider) ListDNSRecords(ctx context.Context, domain string) ([]D
8989
type httpsRecord struct {
9090
Priority int64 `json:"priority"`
9191
Target string `json:"target"`
92-
Params string `json:"params,omitempty"`
92+
Params string `json:"params"`
9393
}
9494

9595
// createDNSRecordRequest embeds the official SDK request but adds HTTPS support
9696
type createDNSRecordRequest struct {
9797
vercelClient.CreateDNSRecordRequest
98+
Value *string `json:"value,omitempty"`
9899
HTTPS *httpsRecord `json:"https,omitempty"`
99100
}
100101

providers/vercel/vercelProvider.go

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ func toVercelCreateRequest(domain string, rc *models.RecordConfig) (createDNSRec
317317
}
318318
req.Name = name
319319
req.Type = rc.Type
320-
req.Value = rc.GetTargetField()
320+
req.Value = ptrString(rc.GetTargetField())
321321
req.TTL = int64(rc.TTL)
322322
req.Comment = ""
323323

@@ -331,17 +331,24 @@ func toVercelCreateRequest(domain string, rc *models.RecordConfig) (createDNSRec
331331
Port: int64(rc.SrvPort),
332332
Target: rc.GetTargetField(),
333333
}
334-
req.Value = "" // SRV uses the SRV struct, not Value
334+
// When dealing with SRV records, we must not set the Value fields,
335+
// otherwise the API throws an error:
336+
// bad_request - Invalid request: should NOT have additional property `value`
337+
req.Value = nil
335338
case "TXT":
336-
req.Value = rc.GetTargetTXTJoined()
339+
req.Value = ptrString(rc.GetTargetTXTJoined())
337340
case "HTTPS":
338341
req.HTTPS = &httpsRecord{
339342
Priority: int64(rc.SvcPriority),
340343
Target: rc.GetTargetField(),
341344
Params: rc.SvcParams,
342345
}
346+
// When dealing with HTTPS records, we must not set the Value fields,
347+
// otherwise the API throws an error:
348+
// bad_request - Invalid request: should NOT have additional property `value`.
349+
req.Value = nil
343350
case "CAA":
344-
req.Value = fmt.Sprintf(`%v %s "%s"`, rc.CaaFlag, rc.CaaTag, rc.GetTargetField())
351+
req.Value = ptrString(fmt.Sprintf(`%v %s "%s"`, rc.CaaFlag, rc.CaaTag, rc.GetTargetField()))
345352
}
346353

347354
return req, nil
@@ -373,7 +380,10 @@ func toVercelUpdateRequest(rc *models.RecordConfig) (updateDNSRecordRequest, err
373380
Port: ptrInt64(int64(rc.SrvPort)),
374381
Target: &value,
375382
}
376-
req.Value = nil // SRV uses the SRV struct, not Value
383+
// When dealing with SRV records, we must not set the Value fields,
384+
// otherwise the API throws an error:
385+
// bad_request - Invalid request: should NOT have additional property `value`
386+
req.Value = nil
377387
case "TXT":
378388
txtValue := rc.GetTargetTXTJoined()
379389
req.Value = &txtValue
@@ -383,6 +393,10 @@ func toVercelUpdateRequest(rc *models.RecordConfig) (updateDNSRecordRequest, err
383393
Target: rc.GetTargetField(),
384394
Params: rc.SvcParams,
385395
}
396+
// When dealing with HTTPS records, we must not set the Value fields,
397+
// otherwise the API throws an error:
398+
// bad_request - Invalid request: should NOT have additional property `value`.
399+
req.Value = nil
386400
case "CAA":
387401
value := fmt.Sprintf(`%v %s "%s"`, rc.CaaFlag, rc.CaaTag, rc.GetTargetField())
388402
req.Value = &value
@@ -395,3 +409,7 @@ func toVercelUpdateRequest(rc *models.RecordConfig) (updateDNSRecordRequest, err
395409
func ptrInt64(v int64) *int64 {
396410
return &v
397411
}
412+
413+
func ptrString(v string) *string {
414+
return &v
415+
}

0 commit comments

Comments
 (0)