{"record":{"id":"79856f80fdb0e675","repo":"shadow1ng/fscan","slug":"s-79856f","errorCode":null,"errorMessage":"%s","messagePattern":"%s","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"plugins/services/credential_tester.go","lineNumber":210,"sourceCode":"}\n\n// TestCredentialsConcurrently 并发测试多个凭据\n// 找到成功凭据后立即通知其他 worker 停止\nfunc TestCredentialsConcurrently(\n\tctx context.Context,\n\tcredentials []Credential,\n\tauthFn AuthFunc,\n\tserviceName string,\n\ttestConfig ConcurrentTestConfig,\n) *ScanResult {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\tif len(credentials) == 0 {\n\t\treturn &ScanResult{\n\t\t\tSuccess: false,\n\t\t\tService: serviceName,\n\t\t\tError:   fmt.Errorf(\"%s\", i18n.GetText(\"service_no_test_creds\")),\n\t\t}\n\t}\n\ttestConfig = normalizeConcurrentTestConfig(testConfig)\n\n\t// TCP 预检：快速验证目标可达，避免对不可达目标浪费全部凭据尝试\n\t// 代理模式下跳过：net.DialTimeout 直连无法到达代理后的内网目标\n\tif testConfig.TargetAddr != \"\" && !testConfig.UseProxy {\n\t\tdialCtx, dialCancel := context.WithTimeout(ctx, 3*time.Second)\n\t\tdefer dialCancel()\n\n\t\tvar dialer net.Dialer\n\t\tpreConn, err := dialer.DialContext(dialCtx, \"tcp\", testConfig.TargetAddr)\n\t\tif err != nil {\n\t\t\treturn &ScanResult{\n\t\t\t\tSuccess: false,\n\t\t\t\tService: serviceName,\n\t\t\t\tError:   fmt.Errorf(i18n.Tr(\"service_target_unreachable\", \"%w\"), err),\n\t\t\t}","sourceCodeStart":192,"sourceCodeEnd":228,"githubUrl":"https://github.com/shadow1ng/fscan/blob/95cc12e753bf43de7004e5aef42a9ffba3934303/plugins/services/credential_tester.go#L192-L228","documentation":"TestCredentialsConcurrently returns this ScanResult when the credential list built for the service is empty, so there is nothing to test. The library treats 'no candidate credentials' as a hard stop rather than silently reporting success. The message text comes from the i18n key service_no_test_creds.","triggerScenarios":"Calling TestCredentialsConcurrently (directly or via Scan) when GenerateCredentials(service, config) yields zero entries — typically because the service name is unknown to the credential generator or config supplies neither custom credentials nor a password dictionary.","commonSituations":"Misspelled service name passed to Scan; custom credential config with an empty credentials list; dictionary files missing so no weak-password candidates are generated; running against a service that has no built-in default credentials.","solutions":["Check that the serviceName string exactly matches a registered service (e.g. 'elasticsearch', 'findnet').","Provide custom credentials in the scan config or ensure the password dictionary file exists and is non-empty.","Log the output of GenerateCredentials(service, config) before calling TestCredentialsConcurrently to confirm it is non-empty.","If an empty credential list is legitimate for your use case, skip the concurrent test call instead of invoking it."],"exampleFix":"// before\ncreds := GenerateCredentials(service, config)\nreturn TestCredentialsConcurrently(ctx, service, creds, authFn, testConfig)\n// after\ncreds := GenerateCredentials(service, config)\nif len(creds) == 0 {\n    return &ScanResult{Success: false, Service: service, Error: errors.New(\"no credentials generated; check config/dictionary\")}\n}\nreturn TestCredentialsConcurrently(ctx, service, creds, authFn, testConfig)","handlingStrategy":"validation","validationCode":"creds := GenerateCredentials(serviceName, config)\nif len(creds) == 0 {\n    return fmt.Errorf(\"no credentials generated for %q; check config and dictionary\", serviceName)\n}","typeGuard":null,"tryCatchPattern":"res := TestCredentialsConcurrently(ctx, svc, creds, fn, cfg)\nif !res.Success && res.Error != nil && res.Error.Error() == i18n.GetText(\"service_no_test_creds\") {\n    log.Printf(\"skipping %s: no credentials\", svc)\n    return nil\n}","preventionTips":["Validate scan config credentials/dictionary paths before starting a scan","Assert service names against the plugin registry","Log GenerateCredentials output during development","Fail fast on empty config lists at config-load time"],"tags":["go","credentials","configuration"],"backgroundTag":"missing-credentials","analyzedSha":"95cc12e753bf43de7004e5aef42a9ffba3934303","analyzedAt":"2026-09-06T17:07:30.094Z","contentChangedAt":"2026-09-06T17:07:30.094Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}