-
Notifications
You must be signed in to change notification settings - Fork 13
fix: validate artifact host for upload and download to address codeql ssrf alerts #207
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -19,6 +19,8 @@ import ( | |||||||||||||||||||||||||||||||
| "github.com/mobile-next/mobilecli/utils" | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| const artifactsHost = "mobilenexthq-artifacts.s3.us-west-2.amazonaws.com" | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| type params map[string]any | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| type RemoteDevice struct { | ||||||||||||||||||||||||||||||||
|
|
@@ -229,8 +231,8 @@ func uploadFileToURL(filePath, uploadURL string) error { | |||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||
| return fmt.Errorf("invalid upload URL: %w", err) | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| if u.Scheme != "https" || u.Host != "mobilenexthq-artifacts.s3.us-west-2.amazonaws.com" { | ||||||||||||||||||||||||||||||||
| return fmt.Errorf("upload URL must be https://mobilenexthq-artifacts.s3.us-west-2.amazonaws.com/..., got: %s", uploadURL) | ||||||||||||||||||||||||||||||||
| if u.Scheme != "https" || u.Hostname() != artifactsHost { | ||||||||||||||||||||||||||||||||
| return fmt.Errorf("upload URL must be https://%s/..., got: %s", artifactsHost, uploadURL) | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| f, err := os.Open(filePath) | ||||||||||||||||||||||||||||||||
|
|
@@ -278,11 +280,13 @@ func downloadFile(downloadURL, outputPath string, cb *ScreenRecordCallbacks) err | |||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||
| return fmt.Errorf("invalid download URL: %w", err) | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| if parsed.Scheme != "https" { | ||||||||||||||||||||||||||||||||
| return fmt.Errorf("download URL must use HTTPS scheme, got %q", parsed.Scheme) | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if parsed.Scheme != "https" || parsed.Hostname() != artifactsHost { | ||||||||||||||||||||||||||||||||
| return fmt.Errorf("download URL must be https://%s/..., got: %s", artifactsHost, downloadURL) | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| resp, err := http.Get(parsed.String()) //nolint:gosec // URL is validated above | ||||||||||||||||||||||||||||||||
| utils.Verbose("downloading from %v", parsed) | ||||||||||||||||||||||||||||||||
| resp, err := http.Get(parsed.String()) | ||||||||||||||||||||||||||||||||
|
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. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Verify unbounded GET usage in Go files and confirm timeout-backed clients.
# Expected:
# 1) No remaining production-path `http.Get(...)` in download/upload flows.
# 2) Download and upload both use `http.Client{Timeout: ...}`.
rg -nP --type=go -C2 '\bhttp\.Get\s*\('
rg -nP --type=go -C2 'var\s+\w+HTTPClient\s*=\s*&http\.Client\s*\{\s*Timeout:'Repository: mobile-next/mobilecli Length of output: 1360 🏁 Script executed: sed -n '220,295p' devices/remote.goRepository: mobile-next/mobilecli Length of output: 2213 Use an HTTP client with explicit timeout for the download request. Line 289 uses ⏱️ Proposed fix (bounded download request)+var downloadHTTPClient = &http.Client{Timeout: 5 * time.Minute}
+
func downloadFile(downloadURL, outputPath string, cb *ScreenRecordCallbacks) error {
parsed, err := url.Parse(downloadURL)
if err != nil {
return fmt.Errorf("invalid download URL: %w", err)
}
if parsed.Scheme != "https" || parsed.Hostname() != artifactsHost {
return fmt.Errorf("download URL must be https://%s/..., got: %s", artifactsHost, downloadURL)
}
utils.Verbose("downloading from %v", parsed)
- resp, err := http.Get(parsed.String())
+ resp, err := downloadHTTPClient.Get(parsed.String())📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||
| return fmt.Errorf("HTTP GET failed: %w", err) | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
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.
Do not log or echo full presigned artifact URLs.
Line 235, Line 285, and Line 288 can expose signed query params in logs/errors. That leaks temporary credentials and weakens the security fix.
🔐 Proposed fix (redact URL details)
Also applies to: 285-285, 288-288
🤖 Prompt for AI Agents