Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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