{"record":{"id":"16b1517af6a934e9","repo":"juanfont/headscale","slug":"s-confirm-form-no-input-tag-for-csrf","errorCode":null,"errorMessage":"%s confirm form: no input tag for CSRF","messagePattern":"(.+?) confirm form: no input tag for CSRF","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"integration/scenario.go","lineNumber":1277,"sourceCode":"\tactionStart := actionIdx + len(`action=\"`)\n\n\tactionEnd := strings.Index(htmlBody[actionStart:], `\"`)\n\tif actionEnd == -1 {\n\t\treturn \"\", nil, fmt.Errorf(\"%s confirm form: unterminated action attribute\", hostname) //nolint:err113\n\t}\n\n\tformAction := htmlBody[actionStart : actionStart+actionEnd]\n\n\t// Extract hidden CSRF input value. The rendered <input> has\n\t// attributes in name-type-value order so we grab the whole tag.\n\tbefore, _, ok := strings.Cut(htmlBody, `name=\"headscale_register_confirm\"`)\n\tif !ok {\n\t\treturn \"\", nil, fmt.Errorf(\"%s confirm form: no CSRF input\", hostname) //nolint:err113\n\t}\n\n\ttagStart := strings.LastIndex(before, \"<input\")\n\tif tagStart == -1 {\n\t\treturn \"\", nil, fmt.Errorf(\"%s confirm form: no input tag for CSRF\", hostname) //nolint:err113\n\t}\n\n\ttagEnd := strings.Index(htmlBody[tagStart:], \">\")\n\tif tagEnd == -1 {\n\t\treturn \"\", nil, fmt.Errorf(\"%s confirm form: unterminated input tag\", hostname) //nolint:err113\n\t}\n\n\tinputTag := htmlBody[tagStart : tagStart+tagEnd+1]\n\n\tvalIdx := strings.Index(inputTag, `value=\"`)\n\tif valIdx == -1 {\n\t\treturn \"\", nil, fmt.Errorf(\"%s confirm form: no value in CSRF input\", hostname) //nolint:err113\n\t}\n\n\tvalStart := valIdx + len(`value=\"`)\n\tvalEnd := strings.Index(inputTag[valStart:], `\"`)\n\tcsrfToken := inputTag[valStart : valStart+valEnd]\n","sourceCodeStart":1259,"sourceCodeEnd":1295,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/integration/scenario.go#L1259-L1295","documentation":"The parser found the CSRF field name (`name=\"headscale_register_confirm\"`) but no `<input` tag opening before it in the preceding HTML. It walks backwards with `strings.LastIndex(before, \"<input\")` to capture the whole tag; failure means the attribute exists outside an input element (or the markup around it is not what the template normally emits).","triggerScenarios":"Produced when the literal `name=\"headscale_register_confirm\"` appears in the body but the text before it contains no `<input` — e.g. the name is rendered inside a <label>, a script string, or the tag is emitted as `<INPUT` with different casing.","commonSituations":"Template refactor moved the hidden input into a partial that renders different markup; attribute appears in an error message echoed to the page; case change in the tag name.","solutions":["Inspect the HTML preceding the CSRF field name to see what element actually carries it.","Make the search case-insensitive (`strings.LastIndex(strings.ToLower(before), \"<input\")`) if only casing changed.","Switch to x/net/html form parsing so element structure is handled properly."],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Prefer an HTML parser; tag-structure questions disappear.","If scraping, match case-insensitively on both tag and attribute names.","Pin template snapshots in tests so markup drift is caught at PR time."],"tags":["html-parsing","integration","csrf"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}