{"record":{"id":"58cc48f171cac702","repo":"flipped-aurora/gin-vue-admin","slug":"url-w","errorCode":null,"errorMessage":"URL 非法: %w","messagePattern":"URL 非法: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/service/system/sys_timed_task_runner.go","lineNumber":129,"sourceCode":"\t\t}()\n\t\tdone <- fn(ctx, json.RawMessage(t.Params))\n\t}()\n\tselect {\n\tcase err := <-done:\n\t\tif err != nil && errors.Is(err, context.DeadlineExceeded) {\n\t\t\treturn \"\", errTaskTimeout\n\t\t}\n\t\treturn \"\", err\n\tcase <-ctx.Done():\n\t\treturn \"\", errTaskTimeout\n\t}\n}\n\n// runHTTP 执行 HTTP 回调(SSRF 防护见 sys_timed_task_http.go)\nfunc (s *TimedTaskService) runHTTP(t system.SysTimedTask) (string, error) {\n\tu, err := url.Parse(t.HttpUrl)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"URL 非法: %w\", err)\n\t}\n\tif u.Scheme != \"http\" && u.Scheme != \"https\" {\n\t\treturn \"\", fmt.Errorf(\"仅允许 http/https, 实际为 %q\", u.Scheme)\n\t}\n\tmethod := strings.ToUpper(strings.TrimSpace(t.HttpMethod))\n\tif method == \"\" {\n\t\tmethod = http.MethodGet\n\t}\n\tvar body io.Reader\n\tif t.HttpBody != \"\" {\n\t\tbody = strings.NewReader(t.HttpBody)\n\t}\n\treq, err := http.NewRequest(method, t.HttpUrl, body)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"构造请求失败: %w\", err)\n\t}\n\tif len(t.HttpHeader) > 0 {\n\t\tvar hdr map[string]string","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/service/system/sys_timed_task_runner.go#L111-L147","documentation":"runHTTP parses t.HttpUrl with url.Parse before issuing the callback. If parsing fails (malformed URL syntax), the run is rejected early with a wrapped \"URL 非法\" error. This is a pre-flight validation step in the SSRF-protected HTTP executor.","triggerScenarios":"t.HttpUrl contains an unparseable value: spaces, missing scheme fragments like '://', control characters, or invalid percent-encoding that url.Parse rejects.","commonSituations":"Admin saved a URL with a trailing space or newline; copied URL with invisible characters; stored empty/garbage http_url column.","solutions":["Correct the task's http_url value to a well-formed absolute URL","Trim whitespace before saving the URL in the admin form","Pre-validate with url.ParseRequestURI in the client code that creates the task"],"exampleFix":"// before\nHttpUrl: \"http:// example.com/hook\"\n// after\nHttpUrl: \"http://example.com/hook\"","handlingStrategy":"validation","validationCode":"u, err := url.Parse(httpUrl)\nif err != nil || u.Host == \"\" {\n    return errors.New(\"callback URL is not a valid absolute URL\")\n}","typeGuard":"func isParseableURL(s string) bool {\n    u, err := url.Parse(s)\n    return err == nil && u.Host != \"\"\n}","tryCatchPattern":"if err := RunTask(t); err != nil {\n    if strings.Contains(err.Error(), \"URL 非法\") {\n        log.Warnf(\"task %d has malformed URL %q\", t.ID, t.HttpUrl)\n    }\n}","preventionTips":["Trim whitespace on URL input fields before saving","Use <input type=\"url\"> or equivalent client-side validation","Store URLs as absolute, fully-qualified values"],"tags":["http","url-validation","timed-task"],"backgroundTag":"invalid-url","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}