{"record":{"id":"9a4ea9f2149ef97f","repo":"Tencent/WeKnora","slug":"unsafe-s3-endpoint-w","errorCode":null,"errorMessage":"unsafe S3 endpoint: %w","messagePattern":"unsafe S3 endpoint: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/application/service/file/s3.go","lineNumber":35,"sourceCode":"\t\"github.com/aws/aws-sdk-go-v2/aws\"\n\t\"github.com/aws/aws-sdk-go-v2/config\"\n\t\"github.com/aws/aws-sdk-go-v2/credentials\"\n\t\"github.com/aws/aws-sdk-go-v2/service/s3\"\n\t\"github.com/aws/aws-sdk-go-v2/service/s3/types\"\n\t\"github.com/google/uuid\"\n)\n\n// s3FileService AWS S3 file service implementation\ntype s3FileService struct {\n\tclient     *s3.Client\n\tbucketName string\n\tpathPrefix string\n}\n\n// newS3Client creates a bare s3FileService with just the SDK client initialised.\nfunc newS3Client(endpoint, accessKey, secretKey, bucketName, region, pathPrefix string, forcePathStyle bool) (*s3FileService, error) {\n\tif err := utils.ValidateURLForSSRF(endpoint); err != nil {\n\t\treturn nil, fmt.Errorf(\"unsafe S3 endpoint: %w\", err)\n\t}\n\tvar cfg aws.Config\n\tvar err error\n\n\t// With no explicit AK/SK, keep the AWS default credential chain intact. This\n\t// supports IAM roles for EC2/ECS/EKS (IRSA), web identity, shared config, and\n\t// environment credentials without persisting long-lived keys in WeKnora.\n\tloadOptions := []func(*config.LoadOptions) error{config.WithRegion(region)}\n\tif accessKey != \"\" || secretKey != \"\" {\n\t\tif accessKey == \"\" || secretKey == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"S3 access key and secret key must be provided together\")\n\t\t}\n\t\tloadOptions = append(loadOptions, config.WithCredentialsProvider(\n\t\t\tcredentials.NewStaticCredentialsProvider(accessKey, secretKey, \"\"),\n\t\t))\n\t}\n\tcfg, err = config.LoadDefaultConfig(context.Background(), loadOptions...)\n","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/application/service/file/s3.go#L17-L53","documentation":"newS3Client runs utils.ValidateURLForSSRF on the configured S3 endpoint before creating the SDK client. This error means the endpoint URL failed SSRF validation — typically a private/loopback/link-local IP, a disallowed scheme, or a non-URL value. WeKnora refuses to build an S3 client against endpoints that could be used for server-side request forgery.","triggerScenarios":"Configuring file storage with an endpoint like http://127.0.0.1:9000, http://169.254.169.254, localhost, an internal 10.x/192.168.x address, or a scheme other than http/https in newS3Client (used by NewS3FileService and connectivity checks).","commonSituations":"Local MinIO/MinIO-style development setups using localhost endpoints, container-internal hostnames resolving to private IPs, typos in the endpoint URL, environments that intentionally allow internal S3-compatible stores but have not widened the SSRF allowlist.","solutions":["Use a publicly resolvable https endpoint, or the internal address is blocked — deploy with an allowed DNS name/IP","If internal S3-compatible storage is intentional, adjust utils.ValidateURLForSSRF allowlist configuration","Check the endpoint value for typos and include the http(s):// scheme","Ensure the endpoint is not accidentally set to metadata or proxy addresses"],"exampleFix":"// before\nsvc, err := NewS3FileService(\"http://127.0.0.1:9000\", ak, sk, bucket, region, prefix) // SSRF check fails\n// after\nsvc, err := NewS3FileService(\"https://s3.example.com\", ak, sk, bucket, region, prefix)","handlingStrategy":"validation","validationCode":"u, err := url.Parse(endpoint)\nif err != nil || (u.Scheme != \"http\" && u.Scheme != \"https\") { return errors.New(\"endpoint must be an absolute http(s) URL\") }\nif err := utils.ValidateURLForSSRF(endpoint); err != nil { return err }","typeGuard":"func isPublicEndpoint(endpoint string) bool {\n    return utils.ValidateURLForSSRF(endpoint) == nil\n}","tryCatchPattern":null,"preventionTips":["Pre-configure vetted endpoints instead of accepting user-supplied URLs","Validate endpoints at config load time, not client construction time","Avoid localhost/link-local/metadata addresses in storage config","Document the SSRF allowlist for teams needing internal MinIO"],"tags":["security","ssrf","s3","config"],"backgroundTag":"ssrf-blocked-endpoint","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}