Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
- Updated images: moved externally-hosted images into repository, ensured images are vertically centred and displayed as new paragraphs, and cropped images to remove blank space (#8053)

### 🔧 Internal changes
- Migrated `image_viewer.jsx` file to use `heic-convert` instead of `heic2any`; Updated CSP exceptions for controllers depending on `image_viewer.jsx` (#8100)
- Migrated `AnnotationUsagePanel` component to use `react-table` v8 (#8021)
- Migrated `SummaryPanel` component to React Table V8 (#8019)
- Resolved/ignored Brakeman warnings and added Brakeman to CI checks (#8066)
Expand Down
5 changes: 1 addition & 4 deletions app/controllers/results_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ class ResultsController < ApplicationController
authorize :criterion_id, through: :criterion_id_param

content_security_policy only: [:edit, :view_marks] do |p|
# required because heic2any uses libheif which calls
# eval (javascript) and creates an image as a blob.
# TODO: remove this when possible
p.script_src :self, "'strict-dynamic'", "'unsafe-eval'"
p.script_src :self, "'strict-dynamic'"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can delete this line for script_src (since we can now just use the default set in content_security_policy.rb). However, you should leave the comment about heic-convert creating images as a blob, which is the reason we still need the p.img_src line below. (But remove the TODO, I don't think there's a good way we can get around this.)

p.img_src :self, :blob
p.frame_src(*SubmissionsController::PERMITTED_IFRAME_SRC)
end
Expand Down
5 changes: 1 addition & 4 deletions app/controllers/submissions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@ class SubmissionsController < ApplicationController

PERMITTED_IFRAME_SRC = ([:self] + %w[https://www.youtube.com https://drive.google.com https://docs.google.com]).freeze
content_security_policy only: [:repo_browser, :file_manager] do |p|
# required because heic2any uses libheif which calls
# eval (javascript) and creates an image as a blob.
# TODO: remove this when possible
p.script_src :self, "'strict-dynamic'", "'unsafe-eval'"
p.script_src :self, "'strict-dynamic'"
p.img_src :self, :blob
p.frame_src(*PERMITTED_IFRAME_SRC)
end
Expand Down
12 changes: 8 additions & 4 deletions app/javascript/Components/Result/image_viewer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,15 @@ export class ImageViewer extends React.PureComponent {
if (["image/heic", "image/heif"].includes(this.props.mime_type)) {
// Returns a promise containing an object URL for a JPEG image converted from the HEIC/HEIF format.
return fetch(this.props.url)
.then(res => res.blob())
.then(blob =>
import("heic2any").then(({default: heic2any}) => heic2any({blob, toType: "image/jpeg"}))
.then(res => res.arrayBuffer())
.then(arrayBuffer => new Uint8Array(arrayBuffer))
.then(inputBuffer =>
import("heic-convert/browser").then(({default: convert}) =>
convert({buffer: inputBuffer, format: "JPEG", quality: 1})
)
)
.then(conversionResult => URL.createObjectURL(conversionResult))
.then(conversionResult => new Blob([conversionResult], {type: "image/jpeg"}))
.then(blob => URL.createObjectURL(blob))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this .then necessary? I assume the Blob constructor is synchronous (doesn't return a Promise) and so we can call UR>LreateObjectURL on it directly.

.then(JPEGObjectURL => this.setState({url: JPEGObjectURL}));
} else {
return new Promise(() => this.setState({url: this.props.url}));
Expand Down
9 changes: 4 additions & 5 deletions config/initializers/content_security_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,14 @@
policy.style_src_elem :self # <style> elements and <link> stylesheets; nonce added below
policy.style_src_attr :unsafe_inline # style="..." attributes (required by Jcrop, jQuery UI, etc.)

# child-src is the worker-src fallback for Safari < 16; blob: is required for heic2any's blob Worker
# and for blob: image URLs created by URL.createObjectURL on those same pages.
# child-src is the worker-src fallback for Safari < 16; blob: is required for blob image URLs
# created by URL.createObjectURL on pages that render converted images.
policy.child_src :self, :blob
policy.worker_src :self, :blob

# Notes:
# 1. The following dependencies still require script-src 'unsafe-eval' on specific pages;
# these are overridden per action in the relevant controllers:
# - heic2any (results, submissions) - libheif compiled via Emscripten uses new Function()
# 1. The following dependency still requires script-src 'unsafe-eval' on a specific page;
# this is overridden per action in the relevant controller:
# - @rjsf/validator-ajv8 (automated_tests#manage) - ajv compiles JSON schemas via new Function()
# 2. @rails/ujs dynamically inserts <script> tags when a controller responses with render js: ...
# These require 'strict-dynamic' to execute.
Expand Down
55 changes: 50 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"dayjs": "^1.11.21",
"dompurify": "^3.4.12",
"flatpickr": "^4.6.13",
"heic2any": "^0.0.4",
"heic-convert": "^2.1.0",
"i18n-js": "^4.5.1",
"jcrop": "^0.0.1",
"jquery": "^3.7.1",
Expand Down