-
Notifications
You must be signed in to change notification settings - Fork 906
Fix proxy permission leak and redact sensitive error descriptions #4748
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
huanghongbo-hhb
wants to merge
12
commits into
koderover:main
Choose a base branch
from
huanghongbo-hhb:fix/sensitive-credential-leaks
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+112
−2
Open
Changes from 6 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
1876e6b
fix: prevent credential leaks in proxy and error responses
huanghongbo-hhb 6b620c2
fix: tighten error sanitization edge cases
huanghongbo-hhb d717b64
test: cover error sanitization behavior
huanghongbo-hhb ef0273d
feat: humanize nacos error messages
huanghongbo-hhb 9df25cc
fix: preserve nacos error chain
huanghongbo-hhb f16c080
fix: humanize remaining nacos client paths
huanghongbo-hhb ab1a550
fix: refine proxy list and nacos job errors
huanghongbo-hhb 9faa3de
chore: keep proxy auth TODO comment
huanghongbo-hhb 384246d
revert: remove nacos error humanizer
huanghongbo-hhb 2726832
fix: revert unintended user orm change
huanghongbo-hhb 6214902
chore: drop error sanitize tests from pr
huanghongbo-hhb ad601cb
fix: keep original nacos error messages
huanghongbo-hhb File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -117,12 +117,12 @@ func (j NacosJobController) Update(useUserInput bool, ticket *commonmodels.Appro | |
|
|
||
| nacosConfigs, err := commonservice.ListNacosConfig(j.jobSpec.NacosID, j.jobSpec.NamespaceID, j.jobSpec.GroupName, log.SugaredLogger()) | ||
| if err != nil { | ||
| return fmt.Errorf("fail to list nacos config: %w", err) | ||
| return err | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 去掉描述是为什么? |
||
| } | ||
|
|
||
| namespaces, err := commonservice.ListNacosNamespace(j.jobSpec.NacosID, log.SugaredLogger()) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to list nacos namespace") | ||
| return err | ||
| } | ||
|
|
||
| namespaceName := "" | ||
|
|
@@ -169,12 +169,12 @@ func (j NacosJobController) Update(useUserInput bool, ticket *commonmodels.Appro | |
| func (j NacosJobController) SetOptions(ticket *commonmodels.ApprovalTicket) error { | ||
| nacosConfigs, err := commonservice.ListNacosConfig(j.jobSpec.NacosID, j.jobSpec.NamespaceID, j.jobSpec.GroupName, log.SugaredLogger()) | ||
| if err != nil { | ||
| return fmt.Errorf("fail to list nacos config: %w", err) | ||
| return err | ||
| } | ||
|
|
||
| namespaces, err := commonservice.ListNacosNamespace(j.jobSpec.NacosID, log.SugaredLogger()) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to list nacos namespace") | ||
| return err | ||
| } | ||
|
|
||
| namespaceName := "" | ||
|
|
@@ -226,7 +226,7 @@ func (j NacosJobController) ToTask(taskID int64) ([]*commonmodels.JobTask, error | |
| } | ||
| client, err := commonservice.GetNacosClient(j.jobSpec.NacosID) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("get nacos client error: %v", err) | ||
| return nil, err | ||
| } | ||
| namespaces, err := client.ListNamespaces() | ||
| if err != nil { | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| package errors | ||
|
|
||
| import ( | ||
| "net/url" | ||
| "regexp" | ||
| "strings" | ||
| ) | ||
|
|
||
| var ( | ||
| urlCandidatePattern = regexp.MustCompile(`https?://[^\s",<>()\[\]{}]+`) | ||
| authorizationHeaderPattern = regexp.MustCompile(`(?i)(authorization[:=]\s*(?:basic|bearer)\s+)[^,\s"]+`) | ||
| sensitiveQueryKeys = map[string]struct{}{ | ||
| "username": {}, | ||
| "user_name": {}, | ||
| "password": {}, | ||
| "passwd": {}, | ||
| "pwd": {}, | ||
| "token": {}, | ||
| "access_token": {}, | ||
| "refresh_token": {}, | ||
| "access_key": {}, | ||
| "access_key_id": {}, | ||
| "access_key_secret": {}, | ||
| "secret": {}, | ||
| "client_secret": {}, | ||
| "private_access_token": {}, | ||
| } | ||
| ) | ||
|
|
||
| func sanitizeSensitiveInfo(text string) string { | ||
| if text == "" { | ||
| return text | ||
| } | ||
|
|
||
| text = sanitizeURLsInText(text) | ||
| text = authorizationHeaderPattern.ReplaceAllString(text, `${1}***`) | ||
|
|
||
| return text | ||
| } | ||
|
|
||
| func sanitizeURLsInText(text string) string { | ||
| return urlCandidatePattern.ReplaceAllStringFunc(text, sanitizeURLString) | ||
| } | ||
|
|
||
| func sanitizeURLString(raw string) string { | ||
| raw = sanitizeURLUserInfo(raw) | ||
| raw = sanitizeURLQuery(raw) | ||
| return raw | ||
| } | ||
|
|
||
| func sanitizeURLUserInfo(raw string) string { | ||
| parsed, err := url.Parse(raw) | ||
| if err != nil || parsed.User == nil { | ||
| return raw | ||
| } | ||
|
|
||
| schemeIdx := strings.Index(raw, "://") | ||
| if schemeIdx < 0 { | ||
| return raw | ||
| } | ||
|
|
||
| userInfoStart := schemeIdx + 3 | ||
| atOffset := strings.Index(raw[userInfoStart:], "@") | ||
| if atOffset < 0 { | ||
| return raw | ||
| } | ||
|
|
||
| replacement := "***" | ||
| if _, hasPassword := parsed.User.Password(); hasPassword { | ||
| replacement = "***:***" | ||
| } | ||
|
|
||
| userInfoEnd := userInfoStart + atOffset | ||
| return raw[:userInfoStart] + replacement + raw[userInfoEnd:] | ||
| } | ||
|
|
||
| func sanitizeURLQuery(raw string) string { | ||
| queryStart := strings.Index(raw, "?") | ||
| if queryStart < 0 { | ||
| return raw | ||
| } | ||
|
|
||
| queryEnd := len(raw) | ||
| if fragmentStart := strings.Index(raw[queryStart+1:], "#"); fragmentStart >= 0 { | ||
| queryEnd = queryStart + 1 + fragmentStart | ||
| } | ||
|
|
||
| queryParts := strings.Split(raw[queryStart+1:queryEnd], "&") | ||
| for i, part := range queryParts { | ||
| key, _, found := strings.Cut(part, "=") | ||
| decodedKey, err := url.QueryUnescape(key) | ||
| if err != nil { | ||
| decodedKey = key | ||
| } | ||
| if _, ok := sensitiveQueryKeys[strings.ToLower(decodedKey)]; !ok { | ||
| continue | ||
| } | ||
| if found { | ||
| queryParts[i] = key + "=***" | ||
| } else { | ||
| queryParts[i] = key | ||
| } | ||
| } | ||
|
|
||
| return raw[:queryStart+1] + strings.Join(queryParts, "&") + raw[queryEnd:] | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| package nacos | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "net/url" | ||
| "regexp" | ||
| "strings" | ||
| ) | ||
|
|
||
| var authStatusPattern = regexp.MustCompile(`(^|[^0-9])(401|403)([^0-9]|$)`) | ||
|
|
||
| type HumanizedError struct { | ||
| message string | ||
| cause error | ||
| } | ||
|
|
||
| func (e *HumanizedError) Error() string { | ||
| return e.message | ||
| } | ||
|
|
||
| func (e *HumanizedError) Unwrap() error { | ||
| return e.cause | ||
| } | ||
|
|
||
| func (e *HumanizedError) Cause() error { | ||
| return e.cause | ||
| } | ||
|
|
||
| func humanizeNacosError(operation, serverAddr string, err error) error { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 日志里最好打印出来真实报错,否则debug难度增大 |
||
| if err == nil { | ||
| return nil | ||
| } | ||
|
|
||
| raw := strings.ToLower(err.Error()) | ||
| addr := displayNacosAddress(serverAddr) | ||
| message := fmt.Sprintf("%s失败:请检查 Nacos 地址、账号密码和服务状态", operation) | ||
|
|
||
| switch { | ||
| case strings.Contains(raw, "parse nacos server address failed"), | ||
| strings.Contains(raw, "missing protocol scheme"), | ||
| strings.Contains(raw, "invalid uri"): | ||
| message = fmt.Sprintf("%s失败:Nacos 地址格式不正确,请检查地址配置", operation) | ||
| case strings.Contains(raw, "unmarshal nacos"), | ||
| strings.Contains(raw, "unmarshal task error"), | ||
| strings.Contains(raw, "cannot unmarshal"), | ||
| strings.Contains(raw, "invalid character"): | ||
| message = fmt.Sprintf("%s失败:Nacos 返回的数据格式异常,请检查服务版本或响应内容", operation) | ||
| case strings.Contains(raw, "no such host"): | ||
| message = fmt.Sprintf("%s失败:无法解析 Nacos 地址 %s,请检查地址是否填写正确", operation, addr) | ||
| case strings.Contains(raw, "certificate signed by unknown authority"): | ||
| message = fmt.Sprintf("%s失败:HTTPS 证书校验失败,请检查 Nacos 服务证书是否受信任", operation) | ||
| case strings.Contains(raw, "x509:"): | ||
| message = fmt.Sprintf("%s失败:HTTPS 证书校验失败,请检查 Nacos 服务证书配置是否正确", operation) | ||
| case strings.Contains(raw, "connection refused"): | ||
| message = fmt.Sprintf("%s失败:连接被拒绝,请检查服务地址、端口或 Nacos 服务状态", operation) | ||
| case strings.Contains(raw, "i/o timeout"), | ||
| strings.Contains(raw, "context deadline exceeded"), | ||
| strings.Contains(raw, "client.timeout exceeded"): | ||
| message = fmt.Sprintf("%s失败:连接超时,请检查网络连通性或 Nacos 服务状态", operation) | ||
| case containsNacosAuthError(raw): | ||
| message = fmt.Sprintf("%s失败:用户名或密码错误,或当前账号无权限访问 Nacos", operation) | ||
| } | ||
|
|
||
| return &HumanizedError{ | ||
| message: message, | ||
| cause: err, | ||
| } | ||
| } | ||
|
|
||
| func containsNacosAuthError(raw string) bool { | ||
| if authStatusPattern.MatchString(raw) { | ||
| return true | ||
| } | ||
|
|
||
| for _, keyword := range []string{ | ||
| "unauthorized", | ||
| "forbidden", | ||
| "unknown user", | ||
| "user not found", | ||
| "invalid password", | ||
| "password error", | ||
| "access denied", | ||
| "permission denied", | ||
| "login failed", | ||
| } { | ||
| if strings.Contains(raw, keyword) { | ||
| return true | ||
| } | ||
| } | ||
|
|
||
| return false | ||
| } | ||
|
|
||
| func displayNacosAddress(serverAddr string) string { | ||
| parsed, err := url.Parse(serverAddr) | ||
| if err == nil && parsed.Host != "" { | ||
| return parsed.Host | ||
| } | ||
|
|
||
| return serverAddr | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
不要改这里,可能会有问题,造成普通用户无法使用代理,真的要改的话也要跟前端确认没问题后再改