juanfont/headscale · error

%s confirm form: no value in CSRF input

Error message

%s confirm form: no value in CSRF input

What it means

The CSRF `<input>` tag was captured whole, but it contains no `value="` attribute, so there is no token to submit with the confirm POST. The template is expected to render attributes in name-type-value order; a missing value means the form was rendered without a token.

Source

Thrown at integration/scenario.go:1289

	if !ok {
		return "", nil, fmt.Errorf("%s confirm form: no CSRF input", hostname) //nolint:err113
	}

	tagStart := strings.LastIndex(before, "<input")
	if tagStart == -1 {
		return "", nil, fmt.Errorf("%s confirm form: no input tag for CSRF", hostname) //nolint:err113
	}

	tagEnd := strings.Index(htmlBody[tagStart:], ">")
	if tagEnd == -1 {
		return "", nil, fmt.Errorf("%s confirm form: unterminated input tag", hostname) //nolint:err113
	}

	inputTag := htmlBody[tagStart : tagStart+tagEnd+1]

	valIdx := strings.Index(inputTag, `value="`)
	if valIdx == -1 {
		return "", nil, fmt.Errorf("%s confirm form: no value in CSRF input", hostname) //nolint:err113
	}

	valStart := valIdx + len(`value="`)
	valEnd := strings.Index(inputTag[valStart:], `"`)
	csrfToken := inputTag[valStart : valStart+valEnd]

	// Build the absolute POST URL from the response's request URL.
	base := prevResp.Request.URL
	confirmURL := &url.URL{
		Scheme: base.Scheme,
		Host:   base.Host,
		Path:   formAction,
	}

	log.Printf("%s auto-submitting confirm form: %s", hostname, confirmURL)

	formData := url.Values{
		"headscale_register_confirm": {csrfToken},

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Print the captured `inputTag` to see exactly how the value attribute is rendered.
  2. Align the parser with the template's current attribute order/quoting.
  3. Use x/net/html to read form values robustly instead of substring matching.
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: `strings.Index(inputTag, "value=\"")` returns -1 on the captured tag. Occurs when the hidden input is rendered with `value=''`, no value at all, or a differently ordered/quoted attribute so the slice from `<input` to `>` missed it.

Common situations: Template change altering attribute order or quoting (e.g. value='...' with single quotes); server renders an empty token because the registration session expired; HTML attribute escaping mangles the value attribute.

Related errors


AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15). Data as JSON: /api/errors/264992248cb5cf4d. Report an issue: GitHub.