-
Notifications
You must be signed in to change notification settings - Fork 0
Automated Test: large-image-processing #330
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 | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -52,14 +52,23 @@ def create_upload(type, file, url) | |||||||||||||||||||||||||||||||||||||
| begin | ||||||||||||||||||||||||||||||||||||||
| # API can provide a URL | ||||||||||||||||||||||||||||||||||||||
| if file.nil? && url.present? && is_api? | ||||||||||||||||||||||||||||||||||||||
| tempfile = FileHelper.download(url, SiteSetting.max_image_size_kb.kilobytes, "discourse-upload-#{type}") rescue nil | ||||||||||||||||||||||||||||||||||||||
| tempfile = FileHelper.download(url, 10.megabytes, "discourse-upload-#{type}") rescue nil | ||||||||||||||||||||||||||||||||||||||
|
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.
If the URL download at line 55 fails ( Proposed guard+ if tempfile.nil?
+ return { errors: ["Failed to download image from URL."] }
+ end
+
# allow users to upload large images...Place this guard after line 61, before the downsizing logic. Also applies to: 72-72 🧰 Tools🪛 RuboCop (1.84.0)[convention] 55-55: Avoid using (Style/RescueModifier) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| filename = File.basename(URI.parse(url).path) | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
54
to
56
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.
If At minimum, log the exception so failed URL uploads are diagnosable: Proposed fix- tempfile = FileHelper.download(url, 10.megabytes, "discourse-upload-#{type}") rescue nil
+ begin
+ tempfile = FileHelper.download(url, 10.megabytes, "discourse-upload-#{type}")
+ rescue => e
+ Rails.logger.warn("Failed to download upload from URL: #{e.message}")
+ tempfile = nil
+ endAlso note: when 📝 Committable suggestion
Suggested change
🧰 Tools🪛 RuboCop (1.84.0)[convention] 55-55: Avoid using (Style/RescueModifier) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||
| tempfile = file.tempfile | ||||||||||||||||||||||||||||||||||||||
| filename = file.original_filename | ||||||||||||||||||||||||||||||||||||||
| content_type = file.content_type | ||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| # allow users to upload large images that will be automatically reduced to allowed size | ||||||||||||||||||||||||||||||||||||||
| if tempfile && tempfile.size > 0 && SiteSetting.max_image_size_kb > 0 && FileHelper.is_image?(filename) | ||||||||||||||||||||||||||||||||||||||
| attempt = 5 | ||||||||||||||||||||||||||||||||||||||
| while attempt > 0 && tempfile.size > SiteSetting.max_image_size_kb.kilobytes | ||||||||||||||||||||||||||||||||||||||
| OptimizedImage.downsize(tempfile.path, tempfile.path, "80%", allow_animation: SiteSetting.allow_animated_thumbnails) | ||||||||||||||||||||||||||||||||||||||
| attempt -= 1 | ||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+63
to
+70
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. Downsize return value is unchecked — failed attempts loop uselessly and may corrupt the file.
Additionally, downsizing in-place ( Proposed fix # allow users to upload large images that will be automatically reduced to allowed size
if tempfile && tempfile.size > 0 && SiteSetting.max_image_size_kb > 0 && FileHelper.is_image?(filename)
attempt = 5
while attempt > 0 && tempfile.size > SiteSetting.max_image_size_kb.kilobytes
- OptimizedImage.downsize(tempfile.path, tempfile.path, "80%", allow_animation: SiteSetting.allow_animated_thumbnails)
- attempt -= 1
+ downsized = OptimizedImage.downsize(tempfile.path, tempfile.path, "80%", allow_animation: SiteSetting.allow_animated_thumbnails)
+ break unless downsized
+ tempfile.rewind
+ attempt -= 1
end
end📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| upload = Upload.create_for(current_user.id, tempfile, filename, tempfile.size, content_type: content_type, image_type: type) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if upload.errors.empty? && current_user.admin? | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -139,25 +139,24 @@ def self.downsize_instructions_animated(from, to, dimensions, opts={}) | |
| end | ||
|
|
||
| def self.resize(from, to, width, height, opts={}) | ||
| optimize("resize", from, to, width, height, opts) | ||
| optimize("resize", from, to, "#{width}x#{height}", opts) | ||
| end | ||
|
|
||
| def self.downsize(from, to, max_width, max_height, opts={}) | ||
| optimize("downsize", from, to, max_width, max_height, opts) | ||
| optimize("downsize", from, to, "#{max_width}x#{max_height}", opts) | ||
| end | ||
|
|
||
| def self.optimize(operation, from, to, width, height, opts={}) | ||
| dim = dimensions(width, height) | ||
| def self.downsize(from, to, dimensions, opts={}) | ||
| optimize("downsize", from, to, dimensions, opts) | ||
| end | ||
|
Comment on lines
145
to
+151
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. Duplicate Ruby does not support method overloading. The second Remove the old overload: Proposed fix- def self.downsize(from, to, max_width, max_height, opts={})
- optimize("downsize", from, to, "#{max_width}x#{max_height}", opts)
- end
-
def self.downsize(from, to, dimensions, opts={})
optimize("downsize", from, to, dimensions, opts)
endIf any callers still rely on the 4-arg form, they must be migrated to pass #!/bin/bash
# Search for all call sites of OptimizedImage.downsize to verify none still use the old 4-arg signature
rg -n 'OptimizedImage\.downsize' --type ruby -C 3🧰 Tools🪛 RuboCop (1.84.0)[warning] 149-149: Method (Lint/DuplicateMethods) 🤖 Prompt for AI Agents |
||
|
|
||
| def self.optimize(operation, from, to, dimensions, opts={}) | ||
| method_name = "#{operation}_instructions" | ||
| method_name += "_animated" if !!opts[:allow_animation] && from =~ /\.GIF$/i | ||
| instructions = self.send(method_name.to_sym, from, to, dim, opts) | ||
| instructions = self.send(method_name.to_sym, from, to, dimensions, opts) | ||
| convert_with(instructions, to) | ||
| end | ||
|
|
||
| def self.dimensions(width, height) | ||
| "#{width}x#{height}" | ||
| end | ||
|
|
||
| def self.convert_with(instructions, to) | ||
| `#{instructions.join(" ")} &> /dev/null` | ||
|
Comment on lines
160
to
161
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. Shell command injection risk via The backtick invocation on line 161 joins user-influenced strings (file paths, dimensions) into a shell command. If any argument contains shell metacharacters (e.g., a crafted filename with Proposed safer alternative def self.convert_with(instructions, to)
- `#{instructions.join(" ")} &> /dev/null`
- return false if $?.exitstatus != 0
+ _output, status = Open3.capture2e(*instructions)
+ return false unless status.success?
ImageOptim.new.optimize_image!(to)
true
rescue
Rails.logger.error("Could not optimize image: #{to}")
false
endThis would require 🧰 Tools🪛 Brakeman (8.0.1)[medium] 161-161: Possible command injection (Command Injection) 🤖 Prompt for AI Agents |
||
| return false if $?.exitstatus != 0 | ||
|
|
||
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.
vardeclaration inside aswitchclause leaks to sibling cases.As flagged by Biome (
noSwitchDeclarations), declaringvar maxSizeKBwithout a block scope insidecase 413makes it accessible to other cases (including fall-through). Wrap the case body in braces.Proposed fix
📝 Committable suggestion
🧰 Tools
🪛 Biome (2.3.13)
[error] 246-246: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Safe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
🤖 Prompt for AI Agents