{"record":{"id":"8953030c04861079","repo":"shadow1ng/fscan","slug":"auth-function-panic-v","errorCode":null,"errorMessage":"auth function panic: %v","messagePattern":"auth function panic: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/services/credential_tester.go","lineNumber":105,"sourceCode":"\t\t}\n\t}\n\tif err := ctx.Err(); err != nil {\n\t\treturn &AuthResult{\n\t\t\tSuccess:   false,\n\t\t\tErrorType: ErrorTypeNetwork,\n\t\t\tError:     err,\n\t\t}\n\t}\n\n\tresultChan := make(chan *AuthResult, 1)\n\n\tgo func() {\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\tresultChan <- &AuthResult{\n\t\t\t\t\tSuccess:   false,\n\t\t\t\t\tErrorType: ErrorTypeUnknown,\n\t\t\t\t\tError:     fmt.Errorf(\"auth function panic: %v\", r),\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\t\tresult := authFn(ctx, cred)\n\t\tresultChan <- result\n\t}()\n\n\tselect {\n\tcase result := <-resultChan:\n\t\treturn result\n\tcase <-ctx.Done():\n\t\t// context 被取消后只做有界等待，避免 authFn 卡死时清理 goroutine 也永久泄漏。\n\t\tgo func() {\n\t\t\ttimer := time.NewTimer(authCleanupWait())\n\t\t\tdefer timer.Stop()\n\n\t\t\tselect {\n\t\t\tcase result := <-resultChan:","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/shadow1ng/fscan/blob/95cc12e753bf43de7004e5aef42a9ffba3934303/plugins/services/credential_tester.go#L87-L123","documentation":"This error is produced by a panic-recovery wrapper around the per-credential auth function. TestSingleCredential runs authFn inside a goroutine with a deferred recover(); if the user-supplied auth function panics (nil map write, nil pointer dereference, index out of range), the panic is converted into a failed AuthResult with ErrorTypeUnknown carrying this message. It indicates a bug inside the auth callback, not a network or credential problem.","triggerScenarios":"Calling Scan/TestCredentialsConcurrently with an authFn that panics on the given ctx/credential — e.g. dereferencing a nil field in the Credential, indexing a slice without bounds checks, or writing to a nil map when parsing the service response.","commonSituations":"Custom auth functions written without nil checks; credentials with empty Username/Password fields the callback assumes are populated; a service banner response that the callback parses unconditionally; refactoring the callback so a field becomes nil at runtime.","solutions":["Inspect the %v payload in the message to find the panic value (usually 'runtime error: nil pointer dereference' or 'index out of range') and fix the offending line in the authFn callback.","Add nil/empty checks for cred.Username, cred.Password and any parsed response fields inside the auth function before using them.","Wrap risky parsing logic inside the authFn in its own recover() if partial results are acceptable.","Test the authFn against empty/zero-value Credential structs in unit tests to surface panics early."],"exampleFix":"// before\nresult := conn.Auth(cred.Username, cred.Password, parseDomains(resp))\n// after\nif cred.Username == \"\" || cred.Password == \"\" {\n    return &AuthResult{Success: false, ErrorType: ErrorTypeAuth, Error: errors.New(\"empty credential\")}\n}\nresult := conn.Auth(cred.Username, cred.Password, parseDomains(resp))","handlingStrategy":"try-catch","validationCode":"if cred == (Credential{}) || authFn == nil {\n    return errors.New(\"invalid credential or auth function\")\n}","typeGuard":"func safeAuthFn(authFn AuthFn) AuthFn {\n    return func(ctx context.Context, cred Credential) *AuthResult {\n        defer func() { _ = recover() }()\n        return authFn(ctx, cred)\n    }\n}","tryCatchPattern":"res := <-resultChan\nif res.Error != nil && strings.HasPrefix(res.Error.Error(), \"auth function panic:\") {\n    // recoverable: log panic payload, mark credential attempt as errored, continue\n    log.Printf(\"authFn panicked: %v\", res.Error)\n    return\n}","preventionTips":["Never dereference Credential fields without nil/empty checks inside authFn","Unit-test authFn with zero-value credentials","Keep parsing of service responses inside functions with their own recover","Run go vet / race detector on custom auth functions"],"tags":["go","panic","concurrency","auth"],"backgroundTag":"internal-invariant-violation","analyzedSha":"95cc12e753bf43de7004e5aef42a9ffba3934303","analyzedAt":"2026-09-06T17:07:30.094Z","contentChangedAt":"2026-09-06T17:07:30.094Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}