Merge branch 'master' into refactor_proforma_import_export
This commit is contained in:
1
.github/workflows/ci.yml
vendored
1
.github/workflows/ci.yml
vendored
@ -70,6 +70,7 @@ jobs:
|
||||
cp config/secrets.yml.ci config/secrets.yml
|
||||
cp config/docker.yml.erb.ci config/docker.yml.erb
|
||||
cp config/mnemosyne.yml.ci config/mnemosyne.yml
|
||||
cp config/content_security_policy.yml.ci config/content_security_policy.yml
|
||||
|
||||
- name: Create database
|
||||
env:
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,6 +4,7 @@
|
||||
/config/mnemosyne.yml
|
||||
/config/secrets.yml
|
||||
/config/docker.yml.erb
|
||||
/config/content_security_policy.yml
|
||||
/coverage
|
||||
/log/*.*
|
||||
/public/assets
|
||||
|
9
Gemfile
9
Gemfile
@ -33,7 +33,7 @@ gem 'prometheus_exporter'
|
||||
gem 'pry-byebug'
|
||||
gem 'puma'
|
||||
gem 'pundit'
|
||||
gem 'rails', '~> 6.1.6'
|
||||
gem 'rails', '~> 6.1.7'
|
||||
gem 'rails_admin', '< 3.0.0' # Blocked by https://github.com/railsadminteam/rails_admin/issues/3490
|
||||
gem 'rails-i18n'
|
||||
gem 'rails-timeago'
|
||||
@ -42,7 +42,7 @@ gem 'rest-client'
|
||||
gem 'rubytree'
|
||||
gem 'rubyzip'
|
||||
gem 'sass-rails'
|
||||
gem 'shakapacker', '6.5.1'
|
||||
gem 'shakapacker', '6.5.2'
|
||||
gem 'slim-rails'
|
||||
gem 'sorcery' # Causes a deprecation warning in Rails 6.0+, see: https://github.com/Sorcery/sorcery/pull/255
|
||||
gem 'telegraf'
|
||||
@ -55,6 +55,10 @@ gem 'mnemosyne-ruby'
|
||||
gem 'sentry-rails'
|
||||
gem 'sentry-ruby'
|
||||
|
||||
group :development do
|
||||
gem 'web-console'
|
||||
end
|
||||
|
||||
group :development, :staging do
|
||||
gem 'better_errors'
|
||||
gem 'binding_of_caller'
|
||||
@ -67,7 +71,6 @@ group :development, :staging do
|
||||
gem 'rubocop-performance'
|
||||
gem 'rubocop-rails', require: false
|
||||
gem 'rubocop-rspec'
|
||||
gem 'web-console'
|
||||
end
|
||||
|
||||
group :development, :test, :staging do
|
||||
|
140
Gemfile.lock
140
Gemfile.lock
@ -13,71 +13,71 @@ GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
ZenTest (4.12.1)
|
||||
actioncable (6.1.6.1)
|
||||
actionpack (= 6.1.6.1)
|
||||
activesupport (= 6.1.6.1)
|
||||
actioncable (6.1.7)
|
||||
actionpack (= 6.1.7)
|
||||
activesupport (= 6.1.7)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailbox (6.1.6.1)
|
||||
actionpack (= 6.1.6.1)
|
||||
activejob (= 6.1.6.1)
|
||||
activerecord (= 6.1.6.1)
|
||||
activestorage (= 6.1.6.1)
|
||||
activesupport (= 6.1.6.1)
|
||||
actionmailbox (6.1.7)
|
||||
actionpack (= 6.1.7)
|
||||
activejob (= 6.1.7)
|
||||
activerecord (= 6.1.7)
|
||||
activestorage (= 6.1.7)
|
||||
activesupport (= 6.1.7)
|
||||
mail (>= 2.7.1)
|
||||
actionmailer (6.1.6.1)
|
||||
actionpack (= 6.1.6.1)
|
||||
actionview (= 6.1.6.1)
|
||||
activejob (= 6.1.6.1)
|
||||
activesupport (= 6.1.6.1)
|
||||
actionmailer (6.1.7)
|
||||
actionpack (= 6.1.7)
|
||||
actionview (= 6.1.7)
|
||||
activejob (= 6.1.7)
|
||||
activesupport (= 6.1.7)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (6.1.6.1)
|
||||
actionview (= 6.1.6.1)
|
||||
activesupport (= 6.1.6.1)
|
||||
actionpack (6.1.7)
|
||||
actionview (= 6.1.7)
|
||||
activesupport (= 6.1.7)
|
||||
rack (~> 2.0, >= 2.0.9)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||
actiontext (6.1.6.1)
|
||||
actionpack (= 6.1.6.1)
|
||||
activerecord (= 6.1.6.1)
|
||||
activestorage (= 6.1.6.1)
|
||||
activesupport (= 6.1.6.1)
|
||||
actiontext (6.1.7)
|
||||
actionpack (= 6.1.7)
|
||||
activerecord (= 6.1.7)
|
||||
activestorage (= 6.1.7)
|
||||
activesupport (= 6.1.7)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (6.1.6.1)
|
||||
activesupport (= 6.1.6.1)
|
||||
actionview (6.1.7)
|
||||
activesupport (= 6.1.7)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
||||
activejob (6.1.6.1)
|
||||
activesupport (= 6.1.6.1)
|
||||
activejob (6.1.7)
|
||||
activesupport (= 6.1.7)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (6.1.6.1)
|
||||
activesupport (= 6.1.6.1)
|
||||
activemodel (6.1.7)
|
||||
activesupport (= 6.1.7)
|
||||
activemodel-serializers-xml (1.0.2)
|
||||
activemodel (> 5.x)
|
||||
activesupport (> 5.x)
|
||||
builder (~> 3.1)
|
||||
activerecord (6.1.6.1)
|
||||
activemodel (= 6.1.6.1)
|
||||
activesupport (= 6.1.6.1)
|
||||
activestorage (6.1.6.1)
|
||||
actionpack (= 6.1.6.1)
|
||||
activejob (= 6.1.6.1)
|
||||
activerecord (= 6.1.6.1)
|
||||
activesupport (= 6.1.6.1)
|
||||
activerecord (6.1.7)
|
||||
activemodel (= 6.1.7)
|
||||
activesupport (= 6.1.7)
|
||||
activestorage (6.1.7)
|
||||
actionpack (= 6.1.7)
|
||||
activejob (= 6.1.7)
|
||||
activerecord (= 6.1.7)
|
||||
activesupport (= 6.1.7)
|
||||
marcel (~> 1.0)
|
||||
mini_mime (>= 1.1.0)
|
||||
activesupport (6.1.6.1)
|
||||
activesupport (6.1.7)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
tzinfo (~> 2.0)
|
||||
zeitwerk (~> 2.3)
|
||||
addressable (2.8.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
addressable (2.8.1)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
amq-protocol (2.3.2)
|
||||
ast (2.4.2)
|
||||
autotest (5.0.0)
|
||||
@ -164,7 +164,7 @@ GEM
|
||||
websocket-driver (>= 0.5.1)
|
||||
ffi (1.15.5)
|
||||
forgery (0.8.1)
|
||||
glob (0.3.0)
|
||||
glob (0.3.1)
|
||||
globalid (1.0.0)
|
||||
activesupport (>= 5.0)
|
||||
haml (5.2.2)
|
||||
@ -179,7 +179,7 @@ GEM
|
||||
domain_name (~> 0.5)
|
||||
i18n (1.12.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
i18n-js (4.0.0)
|
||||
i18n-js (4.0.1)
|
||||
glob
|
||||
i18n
|
||||
image_processing (1.12.2)
|
||||
@ -206,7 +206,7 @@ GEM
|
||||
hana (~> 1.3)
|
||||
regexp_parser (~> 2.0)
|
||||
uri_template (~> 0.7)
|
||||
jwt (2.4.1)
|
||||
jwt (2.5.0)
|
||||
kaminari (1.2.2)
|
||||
activesupport (>= 4.1.0)
|
||||
kaminari-actionview (= 1.2.2)
|
||||
@ -254,7 +254,7 @@ GEM
|
||||
mnemosyne-ruby (1.13.0)
|
||||
activesupport (>= 4)
|
||||
bunny
|
||||
msgpack (1.5.4)
|
||||
msgpack (1.5.6)
|
||||
multi_json (1.15.0)
|
||||
multi_xml (0.6.0)
|
||||
nested_form (0.3.2)
|
||||
@ -281,7 +281,7 @@ GEM
|
||||
racc (~> 1.4)
|
||||
nyan-cat-formatter (0.12.0)
|
||||
rspec (>= 2.99, >= 2.14.2, < 4)
|
||||
oauth (0.5.10)
|
||||
oauth (0.5.14)
|
||||
oauth2 (1.4.10)
|
||||
faraday (>= 0.17.3, < 3.0)
|
||||
jwt (>= 1.0, < 3.0)
|
||||
@ -305,8 +305,8 @@ GEM
|
||||
pry (>= 0.13, < 0.15)
|
||||
pry-rails (0.3.9)
|
||||
pry (>= 0.10.4)
|
||||
public_suffix (4.0.7)
|
||||
puma (5.6.4)
|
||||
public_suffix (5.0.0)
|
||||
puma (5.6.5)
|
||||
nio4r (~> 2.0)
|
||||
pundit (2.2.0)
|
||||
activesupport (>= 3.0.0)
|
||||
@ -321,20 +321,20 @@ GEM
|
||||
rack
|
||||
rack-test (2.0.2)
|
||||
rack (>= 1.3)
|
||||
rails (6.1.6.1)
|
||||
actioncable (= 6.1.6.1)
|
||||
actionmailbox (= 6.1.6.1)
|
||||
actionmailer (= 6.1.6.1)
|
||||
actionpack (= 6.1.6.1)
|
||||
actiontext (= 6.1.6.1)
|
||||
actionview (= 6.1.6.1)
|
||||
activejob (= 6.1.6.1)
|
||||
activemodel (= 6.1.6.1)
|
||||
activerecord (= 6.1.6.1)
|
||||
activestorage (= 6.1.6.1)
|
||||
activesupport (= 6.1.6.1)
|
||||
rails (6.1.7)
|
||||
actioncable (= 6.1.7)
|
||||
actionmailbox (= 6.1.7)
|
||||
actionmailer (= 6.1.7)
|
||||
actionpack (= 6.1.7)
|
||||
actiontext (= 6.1.7)
|
||||
actionview (= 6.1.7)
|
||||
activejob (= 6.1.7)
|
||||
activemodel (= 6.1.7)
|
||||
activerecord (= 6.1.7)
|
||||
activestorage (= 6.1.7)
|
||||
activesupport (= 6.1.7)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 6.1.6.1)
|
||||
railties (= 6.1.7)
|
||||
sprockets-rails (>= 2.0.0)
|
||||
rails-controller-testing (1.0.5)
|
||||
actionpack (>= 5.0.1.rc1)
|
||||
@ -363,9 +363,9 @@ GEM
|
||||
rails (>= 5.0, < 7)
|
||||
remotipart (~> 1.3)
|
||||
sassc-rails (>= 1.3, < 3)
|
||||
railties (6.1.6.1)
|
||||
actionpack (= 6.1.6.1)
|
||||
activesupport (= 6.1.6.1)
|
||||
railties (6.1.7)
|
||||
actionpack (= 6.1.7)
|
||||
activesupport (= 6.1.7)
|
||||
method_source
|
||||
rake (>= 12.2)
|
||||
thor (~> 1.0)
|
||||
@ -375,7 +375,7 @@ GEM
|
||||
activerecord (>= 6.1.5)
|
||||
activesupport (>= 6.1.5)
|
||||
i18n
|
||||
rb-fsevent (0.11.1)
|
||||
rb-fsevent (0.11.2)
|
||||
rb-inotify (0.10.1)
|
||||
ffi (~> 1.0)
|
||||
rbtree (0.4.5)
|
||||
@ -414,7 +414,7 @@ GEM
|
||||
rspec-mocks (~> 3.10)
|
||||
rspec-support (~> 3.10)
|
||||
rspec-support (3.11.0)
|
||||
rubocop (1.35.0)
|
||||
rubocop (1.36.0)
|
||||
json (~> 2.3)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 3.1.2.1)
|
||||
@ -463,8 +463,8 @@ GEM
|
||||
sentry-ruby (~> 5.4.2)
|
||||
sentry-ruby (5.4.2)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
set (1.0.2)
|
||||
shakapacker (6.5.1)
|
||||
set (1.0.3)
|
||||
shakapacker (6.5.2)
|
||||
activesupport (>= 5.2)
|
||||
rack-proxy (>= 0.6.1)
|
||||
railties (>= 5.2)
|
||||
@ -499,9 +499,9 @@ GEM
|
||||
actionpack (>= 5.2)
|
||||
activesupport (>= 5.2)
|
||||
sprockets (>= 3.0.0)
|
||||
ssrf_filter (1.0.8)
|
||||
ssrf_filter (1.1.1)
|
||||
strscan (3.0.4)
|
||||
telegraf (2.1.0)
|
||||
telegraf (2.1.1)
|
||||
influxdb
|
||||
temple (0.8.2)
|
||||
thor (1.2.1)
|
||||
@ -590,7 +590,7 @@ DEPENDENCIES
|
||||
puma
|
||||
pundit
|
||||
rack-mini-profiler
|
||||
rails (~> 6.1.6)
|
||||
rails (~> 6.1.7)
|
||||
rails-controller-testing
|
||||
rails-i18n
|
||||
rails-timeago
|
||||
@ -611,7 +611,7 @@ DEPENDENCIES
|
||||
selenium-webdriver
|
||||
sentry-rails
|
||||
sentry-ruby
|
||||
shakapacker (= 6.5.1)
|
||||
shakapacker (= 6.5.2)
|
||||
shoulda-matchers
|
||||
simplecov
|
||||
slim-rails
|
||||
|
@ -32,4 +32,7 @@ $.jstree.defaults.core.worker = false;
|
||||
// See https://github.com/rails/jquery-ujs/issues/456 for details
|
||||
$(document).on('turbolinks:load', function(){
|
||||
$.rails.refreshCSRFTokens();
|
||||
$('.reloadCurrentPage').on('click', function() {
|
||||
window.location.reload();
|
||||
});
|
||||
});
|
||||
|
@ -78,22 +78,12 @@ var CodeOceanEditor = {
|
||||
if ($('#output-' + index).isPresent()) {
|
||||
return $('#output-' + index);
|
||||
} else {
|
||||
var element = $('<pre class="mb-2">').attr('id', 'output-' + index);
|
||||
var element = $('<div class="mb-2 output-element">').attr('id', 'output-' + index);
|
||||
$('#output').append(element);
|
||||
return element;
|
||||
}
|
||||
},
|
||||
|
||||
findOrCreateRenderElement: function (index) {
|
||||
if ($('#render-' + index).isPresent()) {
|
||||
return $('#render-' + index);
|
||||
} else {
|
||||
var element = $('<div>').attr('id', 'render-' + index);
|
||||
$('#render').append(element);
|
||||
return element;
|
||||
}
|
||||
},
|
||||
|
||||
getCardClass: function (result) {
|
||||
if (result.file_role === 'teacher_defined_linter') {
|
||||
return 'card bg-info text-white'
|
||||
@ -648,7 +638,7 @@ var CodeOceanEditor = {
|
||||
|
||||
augmentStacktraceInOutput: function () {
|
||||
if (this.tracepositions_regex) {
|
||||
$('#output>pre').each($.proxy(function(index, element) {
|
||||
$('#output > .output-element').each($.proxy(function(index, element) {
|
||||
element = $(element)
|
||||
|
||||
const text = _.escape(element.text());
|
||||
@ -656,16 +646,11 @@ var CodeOceanEditor = {
|
||||
|
||||
let matches;
|
||||
|
||||
// Switch both lines below to enable the output of images and render <IMG/> tags.
|
||||
// Also consider `printOutput` in evaluation.js
|
||||
|
||||
// let augmented_text = element.text();
|
||||
let augmented_text = element.html();
|
||||
while (matches = this.tracepositions_regex.exec(text)) {
|
||||
const frame = $('div.frame[data-filename="' + matches[1] + '"]')
|
||||
|
||||
if (frame.length > 0) {
|
||||
// augmented_text = augmented_text.replace(new RegExp(matches[0], 'g'), "<a href='#' data-file='" + matches[1] + "' data-line='" + matches[2] + "'>" + matches[0] + "</a>");
|
||||
augmented_text = augmented_text.replace(new RegExp(_.unescape(matches[0]), 'g'), "<a href='#' data-file='" + matches[1] + "' data-line='" + matches[2] + "'>" + matches[0] + "</a>");
|
||||
}
|
||||
}
|
||||
|
@ -174,11 +174,6 @@ CodeOceanEditorEvaluation = {
|
||||
/**
|
||||
* Output-Logic
|
||||
*/
|
||||
renderWebsocketOutput: function (msg) {
|
||||
var element = this.findOrCreateRenderElement(0);
|
||||
element.append(msg.data);
|
||||
},
|
||||
|
||||
printWebsocketOutput: function (msg) {
|
||||
if (!msg.data || msg.data === "\r") {
|
||||
return;
|
||||
@ -189,7 +184,7 @@ CodeOceanEditorEvaluation = {
|
||||
},
|
||||
|
||||
clearOutput: function () {
|
||||
$('#output pre').remove();
|
||||
$('#output > .output-element').remove();
|
||||
CodeOceanEditorTurtle.hideCanvas();
|
||||
},
|
||||
|
||||
@ -207,43 +202,54 @@ CodeOceanEditorEvaluation = {
|
||||
return;
|
||||
}
|
||||
|
||||
if (output.stdout !== undefined && !output.stdout.startsWith("<img")) {
|
||||
output.stdout = _.escape(output.stdout);
|
||||
const sanitizedStdout = this.sanitizeOutput(output.stdout);
|
||||
const sanitizedStderr = this.sanitizeOutput(output.stderr);
|
||||
|
||||
const element = this.findOrCreateOutputElement(index);
|
||||
const pre = $('<span>');
|
||||
|
||||
if (sanitizedStdout !== '') {
|
||||
if (colorize) {
|
||||
pre.addClass('text-success');
|
||||
}
|
||||
pre.append(sanitizedStdout)
|
||||
}
|
||||
|
||||
var element = this.findOrCreateOutputElement(index);
|
||||
// Switch all four lines below to enable the output of images and render <IMG/> tags.
|
||||
// Also consider `augmentStacktraceInOutput` in editor.js.erb
|
||||
if (!colorize) {
|
||||
if (output.stdout !== undefined && output.stdout !== '') {
|
||||
output.stdout = output.stdout.replace(this.nonPrintableRegEx, "")
|
||||
|
||||
element.append(output.stdout)
|
||||
//element.text(element.text() + output.stdout)
|
||||
if (sanitizedStderr !== '') {
|
||||
if (colorize) {
|
||||
pre.addClass('text-warning');
|
||||
} else {
|
||||
pre.append('StdErr: ');
|
||||
}
|
||||
|
||||
if (output.stderr !== undefined && output.stderr !== '') {
|
||||
output.stderr = output.stderr.replace(this.nonPrintableRegEx, "")
|
||||
|
||||
element.append('StdErr: ' + output.stderr);
|
||||
//element.text('StdErr: ' + element.text() + output.stderr);
|
||||
}
|
||||
|
||||
} else if (output.stderr) {
|
||||
output.stderr = output.stderr.replace(this.nonPrintableRegEx, "")
|
||||
|
||||
element.addClass('text-warning').append(output.stderr);
|
||||
//element.addClass('text-warning').text(element.text() + output.stderr);
|
||||
this.QaApiOutputBuffer.stderr += output.stderr;
|
||||
} else if (output.stdout) {
|
||||
output.stdout = output.stdout.replace(this.nonPrintableRegEx, "")
|
||||
|
||||
element.addClass('text-success').append(output.stdout);
|
||||
//element.addClass('text-success').text(element.text() + output.stdout);
|
||||
this.QaApiOutputBuffer.stdout += output.stdout;
|
||||
} else {
|
||||
element.addClass('text-muted').text($('#output').data('message-no-output'));
|
||||
pre.append(sanitizedStderr);
|
||||
}
|
||||
|
||||
if (sanitizedStdout === '' && sanitizedStderr === '') {
|
||||
if (colorize) {
|
||||
pre.addClass('text-muted');
|
||||
}
|
||||
pre.text($('#output').data('message-no-output'))
|
||||
}
|
||||
|
||||
element.append(pre);
|
||||
},
|
||||
|
||||
sanitizeOutput: function (rawContent) {
|
||||
let sanitizedContent = _.escape(rawContent).replace(this.nonPrintableRegEx, "");
|
||||
|
||||
if (rawContent !== undefined && rawContent.trim().startsWith("<img")) {
|
||||
const doc = new DOMParser().parseFromString(rawContent, "text/html");
|
||||
// Get the parsed element, it is automatically wrapped in a <html><body> document
|
||||
const parsedElement = doc.firstChild.lastChild.firstChild;
|
||||
|
||||
if (parsedElement.src.startsWith("data:image")) {
|
||||
const sanitizedImg = document.createElement('img');
|
||||
sanitizedImg.src = parsedElement.src;
|
||||
sanitizedContent = sanitizedImg.outerHTML;
|
||||
}
|
||||
}
|
||||
|
||||
return sanitizedContent;
|
||||
},
|
||||
|
||||
getDeadlineInformation: function(deadline, translation_key, otherwise) {
|
||||
|
@ -44,7 +44,7 @@ CodeOceanEditorWebsocket = {
|
||||
this.websocket.on('clear', this.clearOutput.bind(this));
|
||||
this.websocket.on('turtle', this.handleTurtleCommand.bind(this));
|
||||
this.websocket.on('turtlebatch', this.handleTurtlebatchCommand.bind(this));
|
||||
this.websocket.on('render', this.renderWebsocketOutput.bind(this));
|
||||
this.websocket.on('render', this.printWebsocketOutput.bind(this));
|
||||
this.websocket.on('exit', this.handleExitCommand.bind(this));
|
||||
this.websocket.on('status', this.showStatus.bind(this));
|
||||
this.websocket.on('hint', this.showHint.bind(this));
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
renderPagedown = function() {
|
||||
$(".wmd-output").each(function (i) {
|
||||
const converter = new Markdown.Converter();
|
||||
const converter = Markdown.getSanitizingConverter();
|
||||
const content = $(this).html();
|
||||
return $(this).html(converter.makeHtml(content));
|
||||
})
|
||||
@ -20,7 +20,7 @@ createPagedownEditor = function( selector, context ) {
|
||||
return;
|
||||
}
|
||||
const attr = $(input).attr('id').split('wmd-input')[1];
|
||||
const converter = new Markdown.Converter();
|
||||
const converter = Markdown.getSanitizingConverter();
|
||||
Markdown.Extra.init(converter);
|
||||
const help = {
|
||||
handler() {
|
||||
|
@ -25,7 +25,7 @@ i.fa-solid, i.fa-regular, i.fa-solid {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
pre {
|
||||
pre, .output-element {
|
||||
background-color: #FAFAFA;
|
||||
margin: 0;
|
||||
padding: .25rem!important;
|
||||
|
@ -20,6 +20,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
#content-left-sidebar, #content-right-sidebar {
|
||||
min-height: 250px;
|
||||
}
|
||||
|
||||
.frame {
|
||||
display: none;
|
||||
@ -77,20 +80,13 @@
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#outputInformation {
|
||||
#output {
|
||||
max-height: 500px;
|
||||
width: 100%;
|
||||
#output {
|
||||
white-space: pre;
|
||||
font-family: var(--bs-font-monospace);
|
||||
font-size: 14px;
|
||||
|
||||
.output-element {
|
||||
overflow: auto;
|
||||
margin: 2em 0;
|
||||
|
||||
p {
|
||||
margin: 0.5em;
|
||||
}
|
||||
|
||||
pre + pre {
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ class ApplicationController < ActionController::Base
|
||||
after_action :verify_authorized, except: %i[welcome]
|
||||
around_action :mnemosyne_trace
|
||||
around_action :switch_locale
|
||||
before_action :set_sentry_context, :allow_iframe_requests, :load_embed_options
|
||||
before_action :set_sentry_context, :load_embed_options
|
||||
protect_from_forgery(with: :exception, prepend: true)
|
||||
rescue_from Pundit::NotAuthorizedError, with: :render_not_authorized
|
||||
rescue_from ActionController::InvalidAuthenticityToken, with: :render_csrf_error
|
||||
@ -40,7 +40,10 @@ class ApplicationController < ActionController::Base
|
||||
token = AuthenticationToken.find_by(shared_secret: params[:token])
|
||||
return unless token
|
||||
|
||||
auto_login(token.user) if token.expire_at.future?
|
||||
if token.expire_at.future?
|
||||
token.update(expire_at: Time.zone.now)
|
||||
auto_login(token.user)
|
||||
end
|
||||
end
|
||||
|
||||
def set_sentry_context
|
||||
@ -93,10 +96,6 @@ class ApplicationController < ActionController::Base
|
||||
# Show root page
|
||||
end
|
||||
|
||||
def allow_iframe_requests
|
||||
response.headers.delete('X-Frame-Options')
|
||||
end
|
||||
|
||||
def load_embed_options
|
||||
@embed_options = if session[:embed_options].present? && session[:embed_options].is_a?(Hash)
|
||||
session[:embed_options].symbolize_keys
|
||||
|
@ -10,6 +10,15 @@ module CodeOcean
|
||||
end
|
||||
private :authorize!
|
||||
|
||||
def show_protected_upload
|
||||
@file = CodeOcean::File.find(params[:id])
|
||||
authorize!
|
||||
raise Pundit::NotAuthorizedError if @embed_options[:disable_download] || @file.name_with_extension != params[:filename]
|
||||
|
||||
real_location = Pathname(@file.native_file.current_path).realpath
|
||||
send_file(real_location, type: @file.native_file.content_type, filename: @file.name_with_extension, disposition: 'attachment')
|
||||
end
|
||||
|
||||
def create
|
||||
@file = CodeOcean::File.new(file_params)
|
||||
if @file.file_template_id
|
||||
|
@ -5,8 +5,14 @@ module FileParameters
|
||||
if exercise && params
|
||||
params.reject do |_, file_attributes|
|
||||
file = CodeOcean::File.find_by(id: file_attributes[:file_id])
|
||||
next true if file.nil? || file.hidden || file.read_only
|
||||
# avoid that public files from other contexts can be created
|
||||
file.nil? || file.hidden || file.read_only || (file.context_type == 'Exercise' && file.context_id != exercise.id) || (file.context_type == 'CommunitySolution' && controller_name != 'community_solutions')
|
||||
# `next` is similar to an early return and will proceed with the next iteration of the loop
|
||||
next true if file.context_type == 'Exercise' && file.context_id != exercise.id
|
||||
next true if file.context_type == 'Submission' && file.context.user != current_user
|
||||
next true if file.context_type == 'CommunitySolution' && controller_name != 'community_solutions'
|
||||
|
||||
false
|
||||
end
|
||||
else
|
||||
[]
|
||||
|
@ -22,6 +22,8 @@ class ExercisesController < ApplicationController
|
||||
skip_after_action :verify_authorized, only: %i[import_exercise import_uuid_check]
|
||||
skip_after_action :verify_policy_scoped, only: %i[import_exercise import_uuid_check], raise: false
|
||||
|
||||
rescue_from Pundit::NotAuthorizedError, with: :not_authorized_for_exercise
|
||||
|
||||
def authorize!
|
||||
authorize(@exercise || @exercises)
|
||||
end
|
||||
@ -294,8 +296,6 @@ class ExercisesController < ApplicationController
|
||||
private :update_exercise_tips
|
||||
|
||||
def implement
|
||||
redirect_to(@exercise, alert: t('exercises.implement.unpublished')) if @exercise.unpublished? && current_user.role != 'admin' && current_user.role != 'teacher' # TODO: TESTESTEST
|
||||
redirect_to(@exercise, alert: t('exercises.implement.no_files')) unless @exercise.files.visible.exists?
|
||||
user_solved_exercise = @exercise.solved_by?(current_user)
|
||||
count_interventions_today = UserExerciseIntervention.where(user: current_user).where('created_at >= ?',
|
||||
Time.zone.now.beginning_of_day).count
|
||||
@ -324,6 +324,8 @@ class ExercisesController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
@embed_options[:disable_score] = true unless @exercise.teacher_defined_assessment?
|
||||
|
||||
@hide_rfc_button = @embed_options[:disable_rfc]
|
||||
|
||||
@search = Search.new
|
||||
@ -432,6 +434,19 @@ class ExercisesController < ApplicationController
|
||||
authorize!
|
||||
end
|
||||
|
||||
def not_authorized_for_exercise(_exception)
|
||||
return render_not_authorized unless current_user
|
||||
return render_not_authorized unless %w[implement working_times intervention search reload].include?(action_name)
|
||||
|
||||
if current_user.admin? || current_user.teacher?
|
||||
redirect_to(@exercise, alert: t('exercises.implement.unpublished')) if @exercise.unpublished?
|
||||
redirect_to(@exercise, alert: t('exercises.implement.no_files')) unless @exercise.files.visible.exists?
|
||||
else
|
||||
render_not_authorized
|
||||
end
|
||||
end
|
||||
private :not_authorized_for_exercise
|
||||
|
||||
def set_execution_environments
|
||||
@execution_environments = ExecutionEnvironment.all.order(:name)
|
||||
end
|
||||
|
@ -9,7 +9,7 @@ class ExternalUsersController < ApplicationController
|
||||
private :authorize!
|
||||
|
||||
def index
|
||||
@search = ExternalUser.ransack(params[:q])
|
||||
@search = ExternalUser.ransack(params[:q], {auth_object: current_user})
|
||||
@users = @search.result.in_study_group_of(current_user).includes(:consumer).paginate(page: params[:page], per_page: per_page_param)
|
||||
authorize!
|
||||
end
|
||||
@ -43,15 +43,15 @@ class ExternalUsersController < ApplicationController
|
||||
(created_at - lag(created_at) over (PARTITION BY user_id, exercise_id
|
||||
ORDER BY created_at)) AS working_time
|
||||
FROM submissions
|
||||
WHERE user_id = #{@user.id}
|
||||
WHERE #{ExternalUser.sanitize_sql(['user_id = ?', @user.id])}
|
||||
AND user_type = 'ExternalUser'
|
||||
#{current_user.admin? ? '' : "AND study_group_id IN (#{current_user.study_groups.pluck(:id).join(', ')}) AND cause = 'submit'"}
|
||||
#{current_user.admin? ? '' : "AND #{ExternalUser.sanitize_sql(['study_group_id IN (?)', current_user.study_groups.pluck(:id)])} AND cause = 'submit'"}
|
||||
GROUP BY exercise_id,
|
||||
user_id,
|
||||
id
|
||||
) AS foo
|
||||
) AS bar
|
||||
#{tag.nil? ? '' : " JOIN exercise_tags et ON et.exercise_id = bar.exercise_id AND et.tag_id = #{tag} "}
|
||||
#{tag.nil? ? '' : " JOIN exercise_tags et ON et.exercise_id = bar.exercise_id AND #{ExternalUser.sanitize_sql(['et.tag_id = ?', tag])}"}
|
||||
GROUP BY user_id,
|
||||
bar.exercise_id;
|
||||
"
|
||||
@ -60,10 +60,14 @@ class ExternalUsersController < ApplicationController
|
||||
def statistics
|
||||
@user = ExternalUser.find(params[:id])
|
||||
authorize!
|
||||
if params[:tag].present?
|
||||
tag = Tag.find(params[:tag])
|
||||
authorize(tag, :show?)
|
||||
end
|
||||
|
||||
statistics = {}
|
||||
|
||||
ApplicationRecord.connection.execute(working_time_query(params[:tag])).each do |tuple|
|
||||
ApplicationRecord.connection.execute(working_time_query(tag&.id)).each do |tuple|
|
||||
statistics[tuple['exercise_id'].to_i] = tuple
|
||||
end
|
||||
|
||||
|
@ -67,7 +67,7 @@ class InternalUsersController < ApplicationController
|
||||
end
|
||||
|
||||
def index
|
||||
@search = InternalUser.ransack(params[:q])
|
||||
@search = InternalUser.ransack(params[:q], {auth_object: current_user})
|
||||
@users = @search.result.includes(:consumer).order(:name).paginate(page: params[:page], per_page: per_page_param)
|
||||
authorize!
|
||||
end
|
||||
|
@ -70,12 +70,9 @@ class ProxyExercisesController < ApplicationController
|
||||
|
||||
def show
|
||||
@search = @proxy_exercise.exercises.ransack
|
||||
@exercises = @proxy_exercise.exercises.ransack.result.order(:title) # @search.result.order(:title)
|
||||
@exercises = @proxy_exercise.exercises.ransack.result.order(:title)
|
||||
end
|
||||
|
||||
# we might want to think about auth here
|
||||
def reload; end
|
||||
|
||||
def update
|
||||
myparams = proxy_exercise_params
|
||||
myparams[:exercises] = Exercise.find(myparams[:exercise_ids].compact_blank)
|
||||
|
@ -12,7 +12,17 @@ class SubmissionsController < ApplicationController
|
||||
before_action :set_testrun, only: %i[run score test]
|
||||
before_action :set_files, only: %i[download show]
|
||||
before_action :set_files_and_specific_file, only: %i[download_file render_file run test]
|
||||
before_action :set_mime_type, only: %i[download_file render_file]
|
||||
before_action :set_content_type_nosniff, only: %i[download download_file render_file]
|
||||
|
||||
# Overwrite the CSP header for the :render_file action
|
||||
content_security_policy only: :render_file do |policy|
|
||||
policy.img_src :none
|
||||
policy.script_src :none
|
||||
policy.font_src :none
|
||||
policy.style_src :none
|
||||
policy.connect_src :none
|
||||
policy.form_action :none
|
||||
end
|
||||
|
||||
def create
|
||||
@submission = Submission.new(submission_params)
|
||||
@ -56,7 +66,11 @@ class SubmissionsController < ApplicationController
|
||||
def download_file
|
||||
raise Pundit::NotAuthorizedError if @embed_options[:disable_download]
|
||||
|
||||
send_data(@file.read, filename: @file.name_with_extension)
|
||||
if @file.native_file?
|
||||
redirect_to protected_upload_path(id: @file.id, filename: @file.name_with_extension)
|
||||
else
|
||||
send_data(@file.content, filename: @file.name_with_extension, disposition: 'attachment')
|
||||
end
|
||||
end
|
||||
|
||||
def index
|
||||
@ -66,13 +80,13 @@ class SubmissionsController < ApplicationController
|
||||
end
|
||||
|
||||
def render_file
|
||||
if @file.native_file?
|
||||
send_data(@file.read, filename: @file.name_with_extension, disposition: 'inline')
|
||||
else
|
||||
render(plain: @file.content)
|
||||
end
|
||||
# If a file should not be downloaded, it should not be rendered either
|
||||
raise Pundit::NotAuthorizedError if @embed_options[:disable_download]
|
||||
|
||||
send_data(@file.read, filename: @file.name_with_extension, disposition: 'inline')
|
||||
end
|
||||
|
||||
# rubocop:disable Metrics/CyclomaticComplexity
|
||||
def run
|
||||
# These method-local socket variables are required in order to use one socket
|
||||
# in the callbacks of the other socket. As the callbacks for the client socket
|
||||
@ -83,7 +97,7 @@ class SubmissionsController < ApplicationController
|
||||
client_socket = tubesock
|
||||
|
||||
client_socket.onopen do |_event|
|
||||
kill_client_socket(client_socket) if @embed_options[:disable_run]
|
||||
return kill_client_socket(client_socket) if @embed_options[:disable_run]
|
||||
end
|
||||
|
||||
client_socket.onclose do |_event|
|
||||
@ -167,7 +181,8 @@ class SubmissionsController < ApplicationController
|
||||
@testrun[:status] = :failed
|
||||
"\n#{t('exercises.implement.exit_failure', timestamp: l(Time.zone.now, format: :short), exit_code: exit_code)}"
|
||||
end
|
||||
send_and_store client_socket, {cmd: :write, stream: :stdout, data: "#{exit_statement}\n"}
|
||||
stream = @testrun[:status] == :ok ? :stdout : :stderr
|
||||
send_and_store client_socket, {cmd: :write, stream: stream, data: "#{exit_statement}\n"}
|
||||
if exit_code == 137
|
||||
send_and_store client_socket, {cmd: :status, status: :out_of_memory}
|
||||
@testrun[:status] = :out_of_memory
|
||||
@ -194,12 +209,13 @@ class SubmissionsController < ApplicationController
|
||||
ensure
|
||||
save_testrun_output 'run'
|
||||
end
|
||||
# rubocop:enable Metrics/CyclomaticComplexity:
|
||||
|
||||
def score
|
||||
hijack do |tubesock|
|
||||
tubesock.onopen do |_event|
|
||||
switch_locale do
|
||||
kill_client_socket(tubesock) if @embed_options[:disable_score]
|
||||
return kill_client_socket(tubesock) if @embed_options[:disable_score] || !@submission.exercise.teacher_defined_assessment?
|
||||
|
||||
# The score is stored separately, we can forward it to the client immediately
|
||||
tubesock.send_data(JSON.dump(@submission.calculate_score))
|
||||
@ -226,7 +242,7 @@ class SubmissionsController < ApplicationController
|
||||
hijack do |tubesock|
|
||||
tubesock.onopen do |_event|
|
||||
switch_locale do
|
||||
kill_client_socket(tubesock) if @embed_options[:disable_run]
|
||||
return kill_client_socket(tubesock) if @embed_options[:disable_run]
|
||||
|
||||
# The score is stored separately, we can forward it to the client immediately
|
||||
tubesock.send_data(JSON.dump(@submission.test(@file)))
|
||||
@ -363,9 +379,9 @@ class SubmissionsController < ApplicationController
|
||||
@files = @submission.collect_files.select(&:visible)
|
||||
end
|
||||
|
||||
def set_mime_type
|
||||
@mime_type = Mime::Type.lookup_by_extension(@file.file_type.file_extension.gsub(/^\./, ''))
|
||||
response.headers['Content-Type'] = @mime_type.to_s
|
||||
def set_content_type_nosniff
|
||||
# When sending a file, we want to ensure that browsers follow our Content-Type header
|
||||
response.headers['X-Content-Type-Options'] = 'nosniff'
|
||||
end
|
||||
|
||||
def set_submission
|
||||
|
@ -74,7 +74,7 @@ class UserExerciseFeedbacksController < ApplicationController
|
||||
|
||||
def update
|
||||
submission = begin
|
||||
current_user.submissions.where(exercise_id: @exercise.id).order('created_at DESC').first
|
||||
current_user.submissions.where(exercise_id: @exercise.id).order('created_at DESC').final.first
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
@ -127,14 +127,16 @@ class UserExerciseFeedbacksController < ApplicationController
|
||||
user_type = current_user.class.name
|
||||
latest_submission = Submission
|
||||
.where(user_id: user_id, user_type: user_type, exercise_id: exercise_id)
|
||||
.order(created_at: :desc).first
|
||||
.order(created_at: :desc).final.first
|
||||
|
||||
authorize(latest_submission, :show?)
|
||||
|
||||
params[:user_exercise_feedback]
|
||||
.permit(:feedback_text, :difficulty, :exercise_id, :user_estimated_worktime)
|
||||
.merge(user_id: user_id,
|
||||
user_type: user_type,
|
||||
submission: latest_submission,
|
||||
normalized_score: latest_submission.normalized_score)
|
||||
normalized_score: latest_submission&.normalized_score)
|
||||
end
|
||||
|
||||
def validate_inputs(uef_params)
|
||||
|
@ -8,7 +8,7 @@ module ExerciseHelper
|
||||
end
|
||||
|
||||
def qa_js_tag
|
||||
javascript_include_tag "#{qa_url}/assets/qa_api.js"
|
||||
javascript_include_tag "#{qa_url}/assets/qa_api.js", integrity: true, crossorigin: 'anonymous'
|
||||
end
|
||||
|
||||
def qa_url
|
||||
|
@ -23,7 +23,7 @@ class UserMailer < ApplicationMailer
|
||||
token = AuthenticationToken.generate!(request_for_comment.user)
|
||||
@receiver_displayname = request_for_comment.user.displayname
|
||||
@commenting_user_displayname = commenting_user.displayname
|
||||
@comment_text = comment.text
|
||||
@comment_text = ERB::Util.html_escape comment.text
|
||||
@rfc_link = request_for_comment_url(request_for_comment, token: token.shared_secret)
|
||||
mail(
|
||||
subject: t('mailers.user_mailer.got_new_comment.subject',
|
||||
@ -35,7 +35,7 @@ class UserMailer < ApplicationMailer
|
||||
token = AuthenticationToken.generate!(subscription.user)
|
||||
@receiver_displayname = subscription.user.displayname
|
||||
@author_displayname = from_user.displayname
|
||||
@comment_text = comment.text
|
||||
@comment_text = ERB::Util.html_escape comment.text
|
||||
@rfc_link = request_for_comment_url(subscription.request_for_comment, token: token.shared_secret)
|
||||
@unsubscribe_link = unsubscribe_subscription_url(subscription)
|
||||
mail(
|
||||
@ -45,10 +45,10 @@ class UserMailer < ApplicationMailer
|
||||
end
|
||||
|
||||
def send_thank_you_note(request_for_comment, receiver)
|
||||
token = AuthenticationToken.generate!(request_for_comment.user)
|
||||
token = AuthenticationToken.generate!(receiver)
|
||||
@receiver_displayname = receiver.displayname
|
||||
@author = request_for_comment.user.displayname
|
||||
@thank_you_note = request_for_comment.thank_you_note
|
||||
@thank_you_note = ERB::Util.html_escape request_for_comment.thank_you_note
|
||||
@rfc_link = request_for_comment_url(request_for_comment, token: token.shared_secret)
|
||||
mail(subject: t('mailers.user_mailer.send_thank_you_note.subject', author: @author), to: receiver.email)
|
||||
end
|
||||
|
@ -4,6 +4,7 @@ class ApplicationRecord < ActiveRecord::Base
|
||||
self.abstract_class = true
|
||||
|
||||
before_validation :strip_strings
|
||||
before_validation :remove_null_bytes
|
||||
|
||||
def strip_strings
|
||||
# trim whitespace from beginning and end of string attributes
|
||||
@ -16,6 +17,15 @@ class ApplicationRecord < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
def remove_null_bytes
|
||||
# remove null bytes from string attributes
|
||||
attribute_names.each do |name|
|
||||
if send(name.to_sym).respond_to?(:tr)
|
||||
send("#{name}=".to_sym, send(name).tr("\0", ''))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.ransackable_associations(_auth_object = nil)
|
||||
[]
|
||||
end
|
||||
|
@ -58,8 +58,7 @@ module CodeOcean
|
||||
|
||||
def read
|
||||
if native_file?
|
||||
valid = Pathname(native_file.current_path).fnmatch? ::File.join(native_file.root, '**')
|
||||
return nil unless valid
|
||||
return nil unless native_file_location_valid?
|
||||
|
||||
native_file.read
|
||||
else
|
||||
@ -67,6 +66,12 @@ module CodeOcean
|
||||
end
|
||||
end
|
||||
|
||||
def native_file_location_valid?
|
||||
real_location = Pathname(native_file.current_path).realpath
|
||||
upload_location = Pathname(::File.join(native_file.root, 'uploads')).realpath
|
||||
real_location.fnmatch? ::File.join(upload_location.to_s, '**')
|
||||
end
|
||||
|
||||
def ancestor_id
|
||||
file_id || id
|
||||
end
|
||||
|
@ -205,6 +205,10 @@ class Exercise < ApplicationRecord
|
||||
"
|
||||
end
|
||||
|
||||
def teacher_defined_assessment?
|
||||
files.any?(&:teacher_defined_assessment?)
|
||||
end
|
||||
|
||||
def get_working_times_for_study_group(study_group_id, user = nil)
|
||||
user_progress = []
|
||||
additional_user_data = []
|
||||
@ -251,7 +255,6 @@ class Exercise < ApplicationRecord
|
||||
end
|
||||
|
||||
def get_quantiles(quantiles)
|
||||
quantiles_str = self.class.sanitize_sql("[#{quantiles.join(',')}]")
|
||||
result = ActiveRecord::Base.transaction do
|
||||
self.class.connection.execute("
|
||||
SET LOCAL intervalstyle = 'iso_8601';
|
||||
@ -358,7 +361,7 @@ class Exercise < ApplicationRecord
|
||||
GROUP BY e.external_id,
|
||||
f.user_id,
|
||||
exercise_id )
|
||||
SELECT unnest(percentile_cont(array#{quantiles_str}) within GROUP (ORDER BY working_time))
|
||||
SELECT unnest(percentile_cont(#{self.class.sanitize_sql(['array[?]', quantiles])}) within GROUP (ORDER BY working_time))
|
||||
FROM result
|
||||
")
|
||||
end
|
||||
|
@ -42,7 +42,11 @@ class User < ApplicationRecord
|
||||
displayname
|
||||
end
|
||||
|
||||
def self.ransackable_attributes(_auth_object = nil)
|
||||
%w[name email external_id consumer_id role]
|
||||
def self.ransackable_attributes(auth_object)
|
||||
if auth_object.admin?
|
||||
%w[name email external_id consumer_id role]
|
||||
else
|
||||
%w[name external_id]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -7,6 +7,8 @@ module CodeOcean
|
||||
end
|
||||
|
||||
def show?
|
||||
return false if @record.native_file? && !@record.native_file_location_valid?
|
||||
|
||||
if @record.context.is_a?(Exercise)
|
||||
admin? || author? || !@record.hidden
|
||||
else
|
||||
@ -14,6 +16,16 @@ module CodeOcean
|
||||
end
|
||||
end
|
||||
|
||||
def show_protected_upload?
|
||||
return false if @record.native_file? && !@record.native_file_location_valid?
|
||||
|
||||
if @record.context.is_a?(Exercise)
|
||||
admin? || author? || (!@record.context.unpublished && !@record.hidden)
|
||||
else
|
||||
admin? || author?
|
||||
end
|
||||
end
|
||||
|
||||
def create?
|
||||
if @record.context.is_a?(Exercise)
|
||||
admin? || author?
|
||||
|
@ -29,8 +29,16 @@ class ExercisePolicy < AdminOrAuthorPolicy
|
||||
define_method(action) { (admin? || teacher_in_study_group? || author?) && @user.codeharbor_link }
|
||||
end
|
||||
|
||||
%i[implement? working_times? intervention? search? submit? reload?].each do |action|
|
||||
define_method(action) { everyone }
|
||||
%i[implement? working_times? intervention? search? reload?].each do |action|
|
||||
define_method(action) do
|
||||
return no_one unless @record.files.visible.exists?
|
||||
|
||||
admin? || teacher_in_study_group? || author? || (everyone && !@record.unpublished?)
|
||||
end
|
||||
end
|
||||
|
||||
def submit?
|
||||
everyone && @record.teacher_defined_assessment?
|
||||
end
|
||||
|
||||
class Scope < Scope
|
||||
@ -39,8 +47,8 @@ class ExercisePolicy < AdminOrAuthorPolicy
|
||||
@scope.all
|
||||
elsif @user.teacher?
|
||||
@scope.where(
|
||||
'user_id IN (SELECT user_id FROM study_group_memberships WHERE study_group_id IN (?))
|
||||
OR (user_id = ? AND user_type = ?)
|
||||
'exercises.user_id IN (SELECT user_id FROM study_group_memberships WHERE study_group_id IN (?))
|
||||
OR (exercises.user_id = ? AND exercises.user_type = ?)
|
||||
OR public = TRUE',
|
||||
@user.study_groups.pluck(:id),
|
||||
@user.id, @user.class.name
|
||||
|
@ -13,10 +13,6 @@ class ProxyExercisePolicy < AdminOrAuthorPolicy
|
||||
define_method(action) { admin? || author? }
|
||||
end
|
||||
|
||||
[:reload?].each do |action|
|
||||
define_method(action) { everyone }
|
||||
end
|
||||
|
||||
class Scope < Scope
|
||||
def resolve
|
||||
if @user.admin?
|
||||
|
@ -79,7 +79,7 @@ div.d-grid id='output_sidebar_uncollapsed' class='d-none col-sm-12 enforce-botto
|
||||
.heading = t('exercises.implement.error_hints.heading')
|
||||
ul.body.mb-0
|
||||
#output
|
||||
pre.overflow-scroll = t('exercises.implement.no_output_yet')
|
||||
.output-element.overflow-scroll = t('exercises.implement.no_output_yet')
|
||||
- if CodeOcean::Config.new(:code_ocean).read[:flowr][:enabled] && !@embed_options[:disable_hints] && !@embed_options[:hide_test_results]
|
||||
#flowrHint.mb-2.card.text-white.bg-info data-url=CodeOcean::Config.new(:code_ocean).read[:flowr][:url] role='tab'
|
||||
.card-header = t('exercises.implement.flowr.heading')
|
||||
|
@ -25,7 +25,7 @@ h1 = link_to_if(policy(@exercise).show?, @exercise, exercise_path(@exercise))
|
||||
span.date = feedback.created_at
|
||||
.card-collapse role="tabpanel"
|
||||
.card-body.feedback
|
||||
.text = render_markdown(feedback.feedback_text)
|
||||
.text style="white-space: pre-wrap;" = feedback.feedback_text
|
||||
.difficulty = "#{t('user_exercise_feedback.difficulty')} #{comment_presets[feedback.difficulty].join(' - ')}" if feedback.difficulty
|
||||
.worktime = "#{t('user_exercise_feedback.working_time')} #{time_presets[feedback.user_estimated_worktime].join(' - ')}" if feedback.user_estimated_worktime
|
||||
- if policy(@exercise).detailed_statistics?
|
||||
@ -36,4 +36,5 @@ h1 = link_to_if(policy(@exercise).show?, @exercise, exercise_path(@exercise))
|
||||
|
||||
= render('shared/pagination', collection: @feedbacks)
|
||||
|
||||
script type="text/javascript" $(function () { $('[data-bs-toggle="tooltip"]').tooltip() });
|
||||
= javascript_tag nonce: true do
|
||||
| $(function () { $('[data-bs-toggle="tooltip"]').tooltip() });
|
||||
|
@ -1,24 +1,32 @@
|
||||
h1 = ExternalUser.model_name.human(count: 2)
|
||||
|
||||
= render(layout: 'shared/form_filters') do |f|
|
||||
.col-md-9.col
|
||||
.row.align-items-center
|
||||
.col
|
||||
= f.label(:name_cont, t('activerecord.attributes.external_user.name'), class: 'visually-hidden form-label')
|
||||
= f.search_field(:name_cont, class: 'form-control', placeholder: t('activerecord.attributes.external_user.name'))
|
||||
.col.mt-0.mt-sm-3.mt-md-0
|
||||
= f.label(:email_cont, t('activerecord.attributes.external_user.email'), class: 'visually-hidden form-label')
|
||||
= f.search_field(:email_cont, class: 'form-control', placeholder: t('activerecord.attributes.external_user.email'))
|
||||
.col.mt-3.mt-lg-0
|
||||
= f.label(:external_id_cont, t('activerecord.attributes.external_user.external_id'), class: 'visually-hidden form-label')
|
||||
= f.search_field(:external_id_cont, class: 'form-control', placeholder: t('activerecord.attributes.external_user.external_id'))
|
||||
.row
|
||||
.col-auto
|
||||
= f.label(:role_eq, t('activerecord.attributes.external_user.role'), class: 'visually-hidden form-label')
|
||||
= f.select(:role_eq, User::ROLES.map { |role| [t("users.roles.#{role}"), role] }, { include_blank: true }, class: 'form-control', prompt: t('activerecord.attributes.external_user.role'))
|
||||
.col-auto.mt-3.mt-lg-0
|
||||
= f.label(:consumer_id_eq, t('activerecord.attributes.external_user.consumer'), class: 'visually-hidden form-label')
|
||||
= f.collection_select(:consumer_id_eq, Consumer.with_external_users, :id, :name, class: 'form-control', prompt: t('activerecord.attributes.external_user.consumer'))
|
||||
- if current_user.admin?
|
||||
.col-md-9.col
|
||||
.row.align-items-center
|
||||
.col
|
||||
= f.label(:name_cont, t('activerecord.attributes.external_user.name'), class: 'visually-hidden form-label')
|
||||
= f.search_field(:name_cont, class: 'form-control', placeholder: t('activerecord.attributes.external_user.name'))
|
||||
.col.mt-0.mt-sm-3.mt-md-0
|
||||
= f.label(:email_cont, t('activerecord.attributes.external_user.email'), class: 'visually-hidden form-label')
|
||||
= f.search_field(:email_cont, class: 'form-control', placeholder: t('activerecord.attributes.external_user.email'))
|
||||
.col.mt-3.mt-lg-0
|
||||
= f.label(:external_id_cont, t('activerecord.attributes.external_user.external_id'), class: 'visually-hidden form-label')
|
||||
= f.search_field(:external_id_cont, class: 'form-control', placeholder: t('activerecord.attributes.external_user.external_id'))
|
||||
.row
|
||||
.col-auto
|
||||
= f.label(:role_eq, t('activerecord.attributes.external_user.role'), class: 'visually-hidden form-label')
|
||||
= f.select(:role_eq, User::ROLES.map { |role| [t("users.roles.#{role}"), role] }, { include_blank: true }, class: 'form-control', prompt: t('activerecord.attributes.external_user.role'))
|
||||
.col-auto.mt-3.mt-lg-0
|
||||
= f.label(:consumer_id_eq, t('activerecord.attributes.external_user.consumer'), class: 'visually-hidden form-label')
|
||||
= f.collection_select(:consumer_id_eq, Consumer.with_external_users, :id, :name, class: 'form-control', prompt: t('activerecord.attributes.external_user.consumer'))
|
||||
- else
|
||||
.col-auto
|
||||
= f.label(:name_cont, t('activerecord.attributes.external_user.name'), class: 'visually-hidden form-label')
|
||||
= f.search_field(:name_cont, class: 'form-control', placeholder: t('activerecord.attributes.external_user.name'))
|
||||
.col-auto
|
||||
= f.label(:external_id_cont, t('activerecord.attributes.external_user.external_id'), class: 'visually-hidden form-label')
|
||||
= f.search_field(:external_id_cont, class: 'form-control', placeholder: t('activerecord.attributes.external_user.external_id'))
|
||||
.table-responsive
|
||||
table.table
|
||||
thead
|
||||
|
@ -9,14 +9,14 @@ html lang="#{I18n.locale || I18n.default_locale}"
|
||||
= favicon_link_tag('/favicon.png', type: 'image/png')
|
||||
= favicon_link_tag('/favicon.png', rel: 'apple-touch-icon', type: 'image/png')
|
||||
= action_cable_meta_tag
|
||||
= stylesheet_pack_tag('application', 'stylesheets', media: 'all', 'data-turbolinks-track': true)
|
||||
= stylesheet_link_tag('application', media: 'all', 'data-turbolinks-track': true)
|
||||
= javascript_pack_tag('application', 'data-turbolinks-track': true, defer: false)
|
||||
= javascript_include_tag('application', 'data-turbolinks-track': true)
|
||||
= stylesheet_pack_tag('application', 'stylesheets', media: 'all', 'data-turbolinks-track': true, integrity: true, crossorigin: 'anonymous')
|
||||
= stylesheet_link_tag('application', media: 'all', 'data-turbolinks-track': true, integrity: true, crossorigin: 'anonymous')
|
||||
= javascript_pack_tag('application', 'data-turbolinks-track': true, defer: false, integrity: true, crossorigin: 'anonymous')
|
||||
= javascript_include_tag('application', 'data-turbolinks-track': true, integrity: true, crossorigin: 'anonymous')
|
||||
= yield(:head)
|
||||
= csrf_meta_tags
|
||||
= timeago_script_tag
|
||||
script type="text/javascript"
|
||||
= timeago_script_tag nonce: true
|
||||
= javascript_tag nonce: true do
|
||||
| I18n.defaultLocale = "#{I18n.default_locale}";
|
||||
| I18n.locale = "#{I18n.locale}";
|
||||
- if SentryJavascript.active?
|
||||
|
@ -1,5 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
json.set! :files do
|
||||
json.array! @exercise.files.visible, :content, :id
|
||||
end
|
@ -79,7 +79,7 @@
|
||||
|
||||
= render('shared/modal', id: 'comment-modal', title: t('exercises.implement.comment.dialogtitle'), template: 'exercises/_comment_dialogcontent')
|
||||
|
||||
javascript:
|
||||
javascript [nonce=content_security_policy_nonce]:
|
||||
|
||||
$('.modal-content').draggable({
|
||||
handle: '.modal-header'
|
||||
|
18
config/content_security_policy.yml.ci
Normal file
18
config/content_security_policy.yml.ci
Normal file
@ -0,0 +1,18 @@
|
||||
default: &default
|
||||
default_src: []
|
||||
|
||||
|
||||
development:
|
||||
<<: *default
|
||||
# Allow the webpack-dev-server in development
|
||||
connect_src:
|
||||
- http://localhost:3035
|
||||
- ws://localhost:3035
|
||||
|
||||
|
||||
production:
|
||||
<<: *default
|
||||
|
||||
|
||||
test:
|
||||
<<: *default
|
33
config/content_security_policy.yml.example
Normal file
33
config/content_security_policy.yml.example
Normal file
@ -0,0 +1,33 @@
|
||||
# This file allows to further customize the Content Security Policy (CSP)
|
||||
# All settings will be applied **in addition** to the application CSP
|
||||
# Default directives are defined here: `initializers/content_security_policy.rb`
|
||||
|
||||
default: &default
|
||||
# Allow the S3 service hosted by the openHPI Cloud to be used for images
|
||||
img_src:
|
||||
- https://s3.xopic.de
|
||||
- https://*.s3.xopic.de
|
||||
- https://s3.openhpicloud.de
|
||||
- https://*.s3.openhpicloud.de
|
||||
# Webkit didn't consider the WSS scheme as part of 'self', adding it explicitly
|
||||
# See https://bugs.webkit.org/show_bug.cgi?id=235873
|
||||
connect_src:
|
||||
- wss://codeocean.openhpi.de
|
||||
# Optionally: Specify a custom, non-Sentry URL for reporting CSP violations
|
||||
# report_uri: https://example.com/csp-report
|
||||
|
||||
|
||||
development:
|
||||
<<: *default
|
||||
# Allow the webpack-dev-server in development
|
||||
connect_src:
|
||||
- http://localhost:3035
|
||||
- ws://localhost:3035
|
||||
|
||||
|
||||
production:
|
||||
<<: *default
|
||||
|
||||
|
||||
test:
|
||||
<<: *default
|
@ -38,7 +38,7 @@ Rails.application.configure do
|
||||
|
||||
# Specifies the header that your server uses for sending files.
|
||||
# config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
|
||||
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
|
||||
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
|
||||
|
||||
# Store uploaded files on the local file system (see config/storage.yml for options).
|
||||
config.active_storage.service = :local
|
||||
@ -50,6 +50,7 @@ Rails.application.configure do
|
||||
|
||||
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
|
||||
config.force_ssl = true
|
||||
config.ssl_options = {hsts: {preload: true}}
|
||||
|
||||
# Include generic and useful information about system operation, but avoid logging too much
|
||||
# information to avoid inadvertent exposure of personally identifiable information (PII).
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'active_support/core_ext/integer/time'
|
||||
|
||||
Rails.application.configure do
|
||||
# Settings specified here will take precedence over those in config/application.rb.
|
||||
|
||||
@ -9,18 +11,15 @@ Rails.application.configure do
|
||||
# since you don't have to restart the web server when you make code changes.
|
||||
config.cache_classes = false
|
||||
|
||||
# true: Eager load code on boot. This eager loads most of Rails and
|
||||
# your application in memory, allowing both threaded web servers
|
||||
# and those relying on copy on write to perform better.
|
||||
# Rake tasks automatically ignore this option for performance.
|
||||
# Eager load code for prometheus exporter
|
||||
# Eager load code on boot. This eager loads most of Rails and
|
||||
# your application in memory, allowing both threaded web servers
|
||||
# and those relying on copy on write to perform better.
|
||||
# Rake tasks automatically ignore this option for performance.
|
||||
# Eager load is also required for the prometheus exporter
|
||||
config.eager_load = true
|
||||
|
||||
# enable web console in staging
|
||||
config.web_console.development_only = false
|
||||
|
||||
# Show full error reports and disable caching.
|
||||
config.consider_all_requests_local = true
|
||||
# Full error reports as well as caching are disabled.
|
||||
config.consider_all_requests_local = false
|
||||
config.action_controller.perform_caching = false
|
||||
|
||||
# Raise an error on page load if there are pending migrations.
|
||||
@ -39,47 +38,50 @@ Rails.application.configure do
|
||||
# For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid.
|
||||
# config.action_dispatch.rack_cache = true
|
||||
|
||||
# Disable Rails's static asset server (Apache or nginx will already do this).
|
||||
config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present?
|
||||
# Disable serving static files from the `/public` folder by default since
|
||||
# Apache or NGINX already handles this.
|
||||
config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
|
||||
|
||||
# Compress JavaScripts and CSS.
|
||||
# config.assets.js_compressor = :uglifier
|
||||
# Compress CSS using a preprocessor.
|
||||
# config.assets.css_compressor = :sass
|
||||
|
||||
# Do not fallback to assets pipeline if a precompiled asset is missed.
|
||||
config.assets.compile = false
|
||||
|
||||
# Generate digests for assets URLs.
|
||||
config.assets.digest = true
|
||||
|
||||
# Version of your assets, change this if you want to expire all your assets.
|
||||
config.assets.version = '1.0'
|
||||
# Enable serving of images, stylesheets, and JavaScripts from an asset server.
|
||||
# config.asset_host = 'http://assets.example.com'
|
||||
|
||||
# Specifies the header that your server uses for sending files.
|
||||
# config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
|
||||
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
|
||||
# config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
|
||||
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
|
||||
|
||||
# Store uploaded files on the local file system (see config/storage.yml for options).
|
||||
config.active_storage.service = :local
|
||||
|
||||
# Mount Action Cable outside main process or domain.
|
||||
# config.action_cable.mount_path = nil
|
||||
# config.action_cable.url = 'wss://example.com/cable'
|
||||
# config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
|
||||
|
||||
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
|
||||
config.force_ssl = true
|
||||
config.ssl_options = {hsts: {preload: true}}
|
||||
|
||||
# Set to :debug to see everything in the log.
|
||||
# Include generic and useful information about system operation, but avoid logging too much
|
||||
# information to avoid inadvertent exposure of personally identifiable information (PII).
|
||||
config.log_level = :info
|
||||
|
||||
# Prepend all log lines with the following tags.
|
||||
# config.log_tags = [ :subdomain, :uuid ]
|
||||
|
||||
# Use a different logger for distributed setups.
|
||||
# config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
|
||||
# config.log_tags = [ :subdomain, :uuid, :request_id ]
|
||||
|
||||
# Use a different cache store in production.
|
||||
# config.cache_store = :mem_cache_store
|
||||
|
||||
# Enable serving of images, stylesheets, and JavaScripts from an asset server.
|
||||
# config.action_controller.asset_host = "http://assets.example.com"
|
||||
# Use a real queuing backend for Active Job (and separate queues per environment).
|
||||
# config.active_job.queue_adapter = :resque
|
||||
# config.active_job.queue_name_prefix = "code_ocean_production"
|
||||
|
||||
# Precompile additional assets.
|
||||
# application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
|
||||
# config.assets.precompile += %w( search.js )
|
||||
config.action_mailer.perform_caching = false
|
||||
|
||||
# Ignore bad email addresses and do not raise email delivery errors.
|
||||
# Set this to true and configure the email server for immediate delivery to raise delivery errors.
|
||||
@ -92,12 +94,46 @@ Rails.application.configure do
|
||||
# Send deprecation notices to registered listeners.
|
||||
config.active_support.deprecation = :notify
|
||||
|
||||
# Disable automatic flushing of the log to improve performance.
|
||||
# config.autoflush_log = false
|
||||
# Log disallowed deprecations.
|
||||
config.active_support.disallowed_deprecation = :log
|
||||
|
||||
# Tell Active Support which deprecation messages to disallow.
|
||||
config.active_support.disallowed_deprecation_warnings = []
|
||||
|
||||
# Use default logging formatter so that PID and timestamp are not suppressed.
|
||||
config.log_formatter = ::Logger::Formatter.new
|
||||
|
||||
# Use a different logger for distributed setups.
|
||||
# require 'syslog/logger'
|
||||
# config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
|
||||
|
||||
if ENV['RAILS_LOG_TO_STDOUT'].present?
|
||||
logger = ActiveSupport::Logger.new($stdout)
|
||||
logger.formatter = config.log_formatter
|
||||
config.logger = ActiveSupport::TaggedLogging.new(logger)
|
||||
end
|
||||
|
||||
# Do not dump schema after migrations.
|
||||
config.active_record.dump_schema_after_migration = false
|
||||
|
||||
# Inserts middleware to perform automatic connection switching.
|
||||
# The `database_selector` hash is used to pass options to the DatabaseSelector
|
||||
# middleware. The `delay` is used to determine how long to wait after a write
|
||||
# to send a subsequent read to the primary.
|
||||
#
|
||||
# The `database_resolver` class is used by the middleware to determine which
|
||||
# database is appropriate to use based on the time delay.
|
||||
#
|
||||
# The `database_resolver_context` class is used by the middleware to set
|
||||
# timestamps for the last write to the primary. The resolver uses the context
|
||||
# class timestamps to determine how long to wait before reading from the
|
||||
# replica.
|
||||
#
|
||||
# By default Rails will store a last write timestamp in the session. The
|
||||
# DatabaseSelector middleware is designed as such you can define your own
|
||||
# strategy for connection switching and pass that into the middleware through
|
||||
# these configuration options.
|
||||
# config.active_record.database_selector = { delay: 2.seconds }
|
||||
# config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
|
||||
# config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
|
||||
end
|
||||
|
@ -6,28 +6,57 @@
|
||||
# For further information see the following documentation
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
|
||||
|
||||
# Rails.application.config.content_security_policy do |policy|
|
||||
# # If you are using webpack-dev-server then specify webpack-dev-server host
|
||||
# policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
|
||||
require_relative 'sentry_csp'
|
||||
require_relative 'sentry_javascript'
|
||||
|
||||
# policy.default_src :self, :https
|
||||
# policy.font_src :self, :https, :data
|
||||
# policy.img_src :self, :https, :data
|
||||
# policy.object_src :none
|
||||
# policy.script_src :self, :https
|
||||
# policy.style_src :self, :https
|
||||
# # If you are using webpack-dev-server then specify webpack-dev-server host
|
||||
# policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
|
||||
def self.apply_yml_settings_for(policy)
|
||||
csp_settings = CodeOcean::Config.new(:content_security_policy)
|
||||
|
||||
# # Specify URI for violation reports
|
||||
# # policy.report_uri "/csp-violation-report-endpoint"
|
||||
# end
|
||||
csp_settings.read.each do |directive, additional_settings|
|
||||
existing_settings = if directive == 'report_uri'
|
||||
''
|
||||
else
|
||||
policy.public_send(directive) || []
|
||||
end
|
||||
all_settings = existing_settings + additional_settings
|
||||
policy.public_send(directive, *all_settings)
|
||||
end
|
||||
end
|
||||
|
||||
def self.apply_sentry_settings_for(policy)
|
||||
sentry_domain = URI.parse SentryJavascript.dsn
|
||||
additional_setting = "#{sentry_domain.scheme}://#{sentry_domain.host}"
|
||||
existing_settings = policy.connect_src || []
|
||||
all_settings = existing_settings + [additional_setting]
|
||||
policy.connect_src(*all_settings)
|
||||
end
|
||||
|
||||
Rails.application.config.content_security_policy do |policy|
|
||||
policy.default_src :none
|
||||
policy.base_uri :self
|
||||
policy.font_src :self
|
||||
# Code executions might return a base64 encoded image as a :data URI
|
||||
policy.img_src :self, :data
|
||||
policy.object_src :none
|
||||
policy.script_src :self, :report_sample
|
||||
# Our ACE editor unfortunately requires :unsafe_inline for the code highlighting
|
||||
policy.style_src :self, :unsafe_inline, :report_sample
|
||||
policy.connect_src :self
|
||||
policy.form_action :self
|
||||
policy.frame_ancestors :none
|
||||
|
||||
# Specify URI for violation reports
|
||||
policy.report_uri SentryCsp.report_url if SentryCsp.active?
|
||||
|
||||
apply_yml_settings_for policy
|
||||
apply_sentry_settings_for policy if SentryJavascript.active?
|
||||
end
|
||||
|
||||
# If you are using UJS then enable automatic nonce generation
|
||||
# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
|
||||
Rails.application.config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
|
||||
|
||||
# Set the nonce only to specific directives
|
||||
# Rails.application.config.content_security_policy_nonce_directives = %w(script-src)
|
||||
Rails.application.config.content_security_policy_nonce_directives = %w[script-src]
|
||||
|
||||
# Report CSP violations to a specified URI
|
||||
# For further information see the following documentation:
|
||||
|
@ -1,12 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Define an application-wide HTTP permissions policy. For further
|
||||
# information see https://developers.google.com/web/updates/2018/06/feature-policy
|
||||
#
|
||||
# Rails.application.config.permissions_policy do |f|
|
||||
# f.camera :none
|
||||
# f.gyroscope :none
|
||||
# f.microphone :none
|
||||
# f.usb :none
|
||||
# f.fullscreen :self
|
||||
# f.payment :self, "https://secure.example.com"
|
||||
# end
|
||||
# TODO: Feature-Policy has been renamed to Permissions-Policy. The Permissions-Policy is
|
||||
# not yet supported by Rails (even though the new name is already used for the method)
|
||||
Rails.application.config.permissions_policy do |policy|
|
||||
policy.accelerometer :none
|
||||
policy.ambient_light_sensor :none
|
||||
policy.autoplay :none
|
||||
policy.camera :none
|
||||
policy.encrypted_media :none
|
||||
policy.fullscreen :none
|
||||
policy.geolocation :none
|
||||
policy.gyroscope :none
|
||||
policy.magnetometer :none
|
||||
policy.microphone :none
|
||||
policy.midi :none
|
||||
policy.payment :none
|
||||
policy.picture_in_picture :none
|
||||
# The `speaker` directive is used for selection of non-default audio output devices
|
||||
policy.speaker :none
|
||||
policy.usb :none
|
||||
policy.vibrate :none
|
||||
policy.vr :none
|
||||
end
|
||||
|
40
config/initializers/sentry_csp.rb
Normal file
40
config/initializers/sentry_csp.rb
Normal file
@ -0,0 +1,40 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'sentry'
|
||||
|
||||
class SentryCsp
|
||||
def self.active?
|
||||
dsn.present? && %w[development test].exclude?(environment)
|
||||
end
|
||||
|
||||
def self.report_url
|
||||
parsed_url = URI.parse dsn
|
||||
|
||||
# Add additional variables to the query string
|
||||
query_params = CGI.parse(parsed_url.query || '')
|
||||
query_params[:sentry_release] = release if release
|
||||
query_params[:sentry_environment] = environment if environment
|
||||
|
||||
# Add the query string back to the URL
|
||||
parsed_url.query = URI.encode_www_form(query_params)
|
||||
|
||||
# Return the full URL
|
||||
parsed_url.to_s
|
||||
end
|
||||
|
||||
class << self
|
||||
private
|
||||
|
||||
def dsn
|
||||
ENV.fetch('SENTRY_CSP_REPORT_URL', nil)
|
||||
end
|
||||
|
||||
def release
|
||||
Sentry.configuration.release
|
||||
end
|
||||
|
||||
def environment
|
||||
Sentry.configuration.environment
|
||||
end
|
||||
end
|
||||
end
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'sentry'
|
||||
|
||||
class SentryJavascript
|
||||
def self.active?
|
||||
dsn.present? && %w[development test].exclude?(environment)
|
||||
|
@ -2,8 +2,23 @@
|
||||
|
||||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
def self.cookie_prefix
|
||||
if (Rails.env.production? || Rails.env.staging?) \
|
||||
&& Rails.application.config.relative_url_root == '/'
|
||||
'__Host-'
|
||||
elsif Rails.env.production? || Rails.env.staging?
|
||||
'__Secure-'
|
||||
else
|
||||
''
|
||||
end
|
||||
end
|
||||
|
||||
Rails.application.config.session_store :cookie_store,
|
||||
key: '_code_ocean_session',
|
||||
key: "#{cookie_prefix}CodeOcean-Session",
|
||||
expire_after: 1.month,
|
||||
secure: Rails.env.production? || Rails.env.staging?,
|
||||
path: Rails.application.config.relative_url_root
|
||||
path: Rails.application.config.relative_url_root,
|
||||
# Signing in through LTI won't work with `SameSite=Strict`
|
||||
# as the cookie is not sent when accessing the `implement` route
|
||||
# following the LTI launch initiated by the LMS as a third party.
|
||||
same_site: :lax
|
||||
|
@ -296,7 +296,7 @@ de:
|
||||
form:
|
||||
hints:
|
||||
command: <em>filename</em> wird automatisch durch den richtigen Dateinamen ersetzt.
|
||||
docker_image: 'Wählen Sie ein Docker-Image aus der Liste oder fügen Sie ein neues hinzu, welches über <a href="https://hub.docker.com/" target="_blank">DockerHub</a> verfügbar ist.'
|
||||
docker_image: 'Wählen Sie ein Docker-Image aus der Liste oder fügen Sie ein neues hinzu, welches über <a href="https://hub.docker.com/" target="_blank" rel="noopener">DockerHub</a> verfügbar ist.'
|
||||
exposed_ports_list: Während der Ausführung sind diese Ports für den Nutzer zugänglich. Die Portnummern müssen nummerisch und mit Komma voneinander getrennt sein.
|
||||
cpu_limit: Geben Sie die Mindestmenge an CPU-Anteilen an, die für jeden Runner reserviert werden soll, gemessen in MHz.
|
||||
errors:
|
||||
@ -340,7 +340,7 @@ de:
|
||||
expand_output_sidebar: Ausgabe-Leiste Ausklappen
|
||||
input: Ihre Eingabe
|
||||
lastsaved: 'Zuletzt gespeichert: '
|
||||
network: 'Während Ihr Code läuft, ist Port %{port} unter folgender Adresse erreichbar: <a href="%{address}" target="_blank">%{address}</a>.'
|
||||
network: 'Während Ihr Code läuft, ist Port %{port} unter folgender Adresse erreichbar: <a href="%{address}" target="_blank" rel="noopener">%{address}</a>.'
|
||||
render: Anzeigen
|
||||
run: Ausführen
|
||||
run_failure: Ihr Code konnte nicht auf der Plattform ausgeführt werden.
|
||||
@ -453,7 +453,7 @@ de:
|
||||
request: "Kommentaranfrage stellen"
|
||||
question: "Bitte beschreiben Sie kurz Ihre Probleme oder nennen Sie den Programmteil, zu dem Sie Feedback wünschen. Ihr Programmcode und eventuelle Fehlermeldungen werden automatisch zur Anfrage hinzugefügt."
|
||||
intervention:
|
||||
explanation: "Diese Meldung erscheint, weil Sie %{duration} Minuten an dieser Aufgabe gearbeitet haben. 25% Ihrer Mitlernenden arbeiten länger daran, insofern ist das kein Problem, aber dies hat sich als <a href='https://hpi.de/forschung/publikationen/dissertationen/dissertation-ralf-teusner.html' style='display: contents;'>effektiven Zeitpunkt für diese Meldung</a> erwiesen."
|
||||
explanation: "Diese Meldung erscheint, weil Sie %{duration} Minuten an dieser Aufgabe gearbeitet haben. 25% Ihrer Mitlernenden arbeiten länger daran, insofern ist das kein Problem, aber dies hat sich als <a href='https://hpi.de/forschung/publikationen/dissertationen/dissertation-ralf-teusner.html' style='display: contents;' target='_blank' rel='noopener'>effektiven Zeitpunkt für diese Meldung</a> erwiesen."
|
||||
rfc_intervention:
|
||||
text: "Falls Sie bei dieser Aufgabe nicht weiterkommen und nicht selbst weiter knobeln möchten, können Ihre Mitlernenden bestimmt helfen! </br> </br>"
|
||||
break_intervention:
|
||||
@ -780,7 +780,7 @@ de:
|
||||
runtime_output: "Programmausgabe"
|
||||
test_results: "Testergebnisse"
|
||||
sessions:
|
||||
expired: Ihre Session ist abgelaufen. Bitte <a href="javascript:window.location.reload(true)">laden Sie diese Seite neu</a> bevor Sie fortfahren.
|
||||
expired: Ihre Session ist abgelaufen. Bitte <a href="" class="reloadCurrentPage">laden Sie diese Seite neu</a> bevor Sie fortfahren.
|
||||
create:
|
||||
failure: Fehlerhafte E-Mail oder Passwort.
|
||||
success: Sie haben sich erfolgreich angemeldet.
|
||||
@ -793,7 +793,7 @@ de:
|
||||
destroy_through_lti:
|
||||
average_score: Durchschnittliche Punktzahl
|
||||
final_submissions: Abgaben anderer Nutzer
|
||||
finished_with_consumer: 'Sie können dieses Fenster nun schließen oder <a href="%{url}">zu %{consumer} zurückkehren</a>.'
|
||||
finished_with_consumer: 'Sie können dieses Fenster nun schließen oder <a href="%{url}" rel="nofollow">zu %{consumer} zurückkehren</a>.'
|
||||
finished_without_consumer: Sie können dieses Fenster nun schließen.
|
||||
headline: Gut gemacht!
|
||||
score: Ihre Punktzahl
|
||||
@ -834,7 +834,7 @@ de:
|
||||
link: Hilfe
|
||||
index: Index
|
||||
message_failure: Leider ist ein Fehler auf unserer Plattform aufgetreten. Bitte probieren Sie es später noch einmal.
|
||||
websocket_failure: Leider ist ein Verbindungsproblem aufgetreten. <a href="https://websocketstest.com">Bitte überprüfen Sie Websocket-Verbindungen mit diesem Tool</a> und versuchen Sie es erneut.
|
||||
websocket_failure: Leider ist ein Verbindungsproblem aufgetreten. <a href="https://websocketstest.com" target="_blank" rel="noopener">Bitte überprüfen Sie Websocket-Verbindungen mit diesem Tool</a> und versuchen Sie es erneut.
|
||||
new: Hinzufügen
|
||||
new_model: '%{model} hinzufügen'
|
||||
number: Nummer
|
||||
|
@ -296,7 +296,7 @@ en:
|
||||
form:
|
||||
hints:
|
||||
command: <em>filename</em> is automatically replaced with the correct filename.
|
||||
docker_image: Pick a Docker image listed above or add a new one which is available via <a href="https://hub.docker.com/" target="_blank">DockerHub</a>.
|
||||
docker_image: Pick a Docker image listed above or add a new one which is available via <a href="https://hub.docker.com/" target="_blank" rel="noopener">DockerHub</a>.
|
||||
exposed_ports_list: During code execution these ports are accessible for the user. Port numbers must be numeric and separated by a comma.
|
||||
cpu_limit: Specify the minimum amount of CPU shares to reserve for each runner, measured in MHz.
|
||||
errors:
|
||||
@ -340,7 +340,7 @@ en:
|
||||
expand_output_sidebar: Expand Output Sidebar
|
||||
input: Your input
|
||||
lastsaved: 'Last saved: '
|
||||
network: 'While your code is running, port %{port} is accessible using the following address: <a href="%{address}" target="_blank">%{address}</a>.'
|
||||
network: 'While your code is running, port %{port} is accessible using the following address: <a href="%{address}" target="_blank" rel="noopener">%{address}</a>.'
|
||||
render: Render
|
||||
run: Run
|
||||
run_failure: Your code could not be run.
|
||||
@ -453,7 +453,7 @@ en:
|
||||
request: "Request Comments"
|
||||
question: 'Please shortly describe your problem or the program part you would like to get feedback for. Your program code and potential error messages are automatically appended to your request.'
|
||||
intervention:
|
||||
explanation: "This message appears because you have been working on this exercise for %{duration} minutes. 25% of your fellow learners took more time to solve the exercise, so in that sense it's not a problem, but this has proven to be an <a href='https://hpi.de/en/research/publications/dissertations/dissertation-ralf-teusner.html' style='display: contents;'>effective time for this message</a>."
|
||||
explanation: "This message appears because you have been working on this exercise for %{duration} minutes. 25% of your fellow learners took more time to solve the exercise, so in that sense it's not a problem, but this has proven to be an <a href='https://hpi.de/en/research/publications/dissertations/dissertation-ralf-teusner.html' style='display: contents;' target='_blank' rel='noopener'>effective time for this message</a>."
|
||||
rfc_intervention:
|
||||
text: "If you are struggling with this exercise and don't want to continue on your own, your fellow learners can help out! </br> </br>"
|
||||
break_intervention:
|
||||
@ -780,7 +780,7 @@ en:
|
||||
runtime_output: "Runtime Output"
|
||||
test_results: "Test Results"
|
||||
sessions:
|
||||
expired: Your session has expired. Please <a href="javascript:window.location.reload(true)">reload this page</a> before continuing.
|
||||
expired: Your session has expired. Please <a href="" class="reloadCurrentPage">reload this page</a> before continuing.
|
||||
create:
|
||||
failure: Invalid email or password.
|
||||
success: Successfully signed in.
|
||||
@ -793,7 +793,7 @@ en:
|
||||
destroy_through_lti:
|
||||
average_score: Average Score
|
||||
final_submissions: Other Users' Submissions
|
||||
finished_with_consumer: 'You may close this window now or <a href="%{url}">return to %{consumer}</a>.'
|
||||
finished_with_consumer: 'You may close this window now or <a href="%{url}" rel="nofollow">return to %{consumer}</a>.'
|
||||
finished_without_consumer: You may close this window now.
|
||||
headline: Well done!
|
||||
score: Your Score
|
||||
@ -834,7 +834,7 @@ en:
|
||||
link: Help
|
||||
index: Index
|
||||
message_failure: 'Sorry, something went wrong.'
|
||||
websocket_failure: Sorry, a connection issue occoured. <a href="https://websocketstest.com">Please check WebSocket connections with this tool</a> and try again.
|
||||
websocket_failure: Sorry, a connection issue occoured. <a href="https://websocketstest.com" target="_blank" rel="noopener">Please check WebSocket connections with this tool</a> and try again.
|
||||
new: Add
|
||||
new_model: 'Add %{model}'
|
||||
number: Number
|
||||
|
@ -128,6 +128,7 @@ Rails.application.routes.draw do
|
||||
namespace :code_ocean do
|
||||
resources :files, only: %i[create destroy]
|
||||
end
|
||||
get '/uploads/files/:id/:filename', to: 'code_ocean/files#show_protected_upload', as: :protected_upload, constraints: {filename: FILENAME_REGEXP}
|
||||
|
||||
resources :file_types
|
||||
|
||||
|
@ -1,12 +1,14 @@
|
||||
// See the shakacode/shakapacker README and docs directory for advice on customizing your webpackConfig.
|
||||
|
||||
const { webpackConfig, merge } = require('shakapacker')
|
||||
const { webpackConfig, config, merge } = require('shakapacker')
|
||||
const webpack = require('webpack');
|
||||
|
||||
const CompressionPlugin = require("compression-webpack-plugin");
|
||||
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
|
||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||
const TerserPlugin = require("terser-webpack-plugin");
|
||||
const WebpackAssetsManifest = require('webpack-assets-manifest');
|
||||
const { SubresourceIntegrityPlugin } = require("webpack-subresource-integrity");
|
||||
|
||||
// Custom ERB loader to disable Spring and prevent crashes
|
||||
const erb = require("./loaders/erb");
|
||||
@ -30,7 +32,9 @@ const envConfig = module.exports = {
|
||||
],
|
||||
},
|
||||
output: {
|
||||
publicPath: relative_url_root + public_output_path
|
||||
publicPath: relative_url_root + public_output_path,
|
||||
// the following setting is required for SRI to work:
|
||||
crossOriginLoading: 'anonymous',
|
||||
},
|
||||
performance: {
|
||||
// Turn off size warnings for large assets
|
||||
@ -53,6 +57,15 @@ const envConfig = module.exports = {
|
||||
}),
|
||||
new CompressionPlugin(),
|
||||
new MiniCssExtractPlugin(),
|
||||
new SubresourceIntegrityPlugin(),
|
||||
new WebpackAssetsManifest({
|
||||
entrypoints: true,
|
||||
integrity: false,
|
||||
writeToDisk: true,
|
||||
entrypointsUseAssets: true,
|
||||
publicPath: true,
|
||||
output: config.manifestPath,
|
||||
})
|
||||
],
|
||||
resolve: {
|
||||
extensions: ['.css', '.ts', '.tsx'],
|
||||
@ -65,4 +78,8 @@ const envConfig = module.exports = {
|
||||
stats: 'minimal',
|
||||
}
|
||||
|
||||
// Use the two lines below to remove the original WebpackAssetsManifest and replace it with our custom config.
|
||||
const filteredPlugins = webpackConfig.plugins.filter((plugin) => !(plugin instanceof WebpackAssetsManifest))
|
||||
webpackConfig.plugins = filteredPlugins;
|
||||
|
||||
module.exports = merge(webpackConfig, envConfig)
|
||||
|
@ -194,7 +194,7 @@ source "$HOME/.profile"
|
||||
- Create all necessary config files:
|
||||
|
||||
```bash
|
||||
for f in action_mailer.yml database.yml secrets.yml code_ocean.yml docker.yml.erb mnemosyne.yml
|
||||
for f in action_mailer.yml database.yml secrets.yml code_ocean.yml docker.yml.erb mnemosyne.yml content_security_policy.yml
|
||||
do
|
||||
if [ ! -f config/$f ]
|
||||
then
|
||||
@ -303,7 +303,7 @@ source "$HOME/.profile"
|
||||
```
|
||||
- Get a local copy of the config files and verify the settings:
|
||||
```shell script
|
||||
for f in action_mailer.yml database.yml secrets.yml code_ocean.yml docker.yml.erb mnemosyne.yml
|
||||
for f in action_mailer.yml database.yml secrets.yml code_ocean.yml docker.yml.erb mnemosyne.yml content_security_policy.yml
|
||||
do
|
||||
if [ ! -f config/$f ]
|
||||
then
|
||||
|
27
lib/webpacker/sri_helper_extensions.rb
Normal file
27
lib/webpacker/sri_helper_extensions.rb
Normal file
@ -0,0 +1,27 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Webpacker::SriHelperExtensions
|
||||
def stylesheet_link_tag(*sources, **options)
|
||||
tags = sources.map do |stylesheet|
|
||||
if stylesheet.is_a?(Hash)
|
||||
super(stylesheet[:src], options.merge(integrity: stylesheet[:integrity]))
|
||||
else
|
||||
super(stylesheet, options)
|
||||
end
|
||||
end
|
||||
safe_join(tags)
|
||||
end
|
||||
|
||||
def javascript_include_tag(*sources, **options)
|
||||
tags = sources.map do |javascript|
|
||||
if javascript.is_a?(Hash)
|
||||
super(javascript[:src], options.merge(integrity: javascript[:integrity]))
|
||||
else
|
||||
super(javascript, options)
|
||||
end
|
||||
end
|
||||
safe_join(tags)
|
||||
end
|
||||
end
|
||||
|
||||
Sprockets::Rails::Helper.prepend(Webpacker::SriHelperExtensions)
|
29
lib/webpacker/sri_manifest_extensions.rb
Normal file
29
lib/webpacker/sri_manifest_extensions.rb
Normal file
@ -0,0 +1,29 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Webpacker::SriManifestExtensions
|
||||
def lookup(name, pack_type = {})
|
||||
asset = super
|
||||
|
||||
augment_with_integrity asset, pack_type
|
||||
end
|
||||
|
||||
def lookup_pack_with_chunks(name, pack_type = {})
|
||||
assets = super
|
||||
|
||||
assets.map do |asset|
|
||||
augment_with_integrity asset, pack_type
|
||||
end
|
||||
end
|
||||
|
||||
def augment_with_integrity(asset, _pack_type = {})
|
||||
if asset.respond_to?(:dig) && asset['integrity']
|
||||
{src: asset['src'], integrity: asset['integrity']}
|
||||
elsif asset.respond_to?(:dig)
|
||||
asset['src']
|
||||
else
|
||||
asset
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Webpacker::Manifest.prepend(Webpacker::SriManifestExtensions)
|
21
package.json
21
package.json
@ -2,17 +2,17 @@
|
||||
"name": "codeocean",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.18.10",
|
||||
"@babel/core": "^7.19.0",
|
||||
"@babel/plugin-transform-runtime": "^7.18.10",
|
||||
"@babel/preset-env": "7",
|
||||
"@babel/runtime": "7",
|
||||
"@egjs/hammerjs": "^2.0.17",
|
||||
"@fortawesome/fontawesome-free": "^6.1.2",
|
||||
"@fortawesome/fontawesome-free": "^6.2.0",
|
||||
"@popperjs/core": "^2.11.6",
|
||||
"@sentry/browser": "^6.11.0",
|
||||
"@sentry/browser": "^7.11.1",
|
||||
"@webpack-cli/serve": "^1.7.0",
|
||||
"babel-loader": "^8.2.5",
|
||||
"bootstrap": "^5.2.0",
|
||||
"bootstrap": "^5.2.1",
|
||||
"bootswatch": "^5.2.0",
|
||||
"chosen-js": "^1.8.7",
|
||||
"component-emitter": "^1.3.0",
|
||||
@ -22,8 +22,8 @@
|
||||
"d3": "^7.6.1",
|
||||
"d3-tip": "^0.9.1",
|
||||
"highlight.js": "^11.5.1",
|
||||
"i18n-js": "^4.0.2",
|
||||
"jquery": "^3.6.0",
|
||||
"i18n-js": "^4.1.1",
|
||||
"jquery": "^3.6.1",
|
||||
"jquery-ui": "^1.13.1",
|
||||
"jquery-ujs": "^1.2.3",
|
||||
"jstree": "^3.3.12",
|
||||
@ -34,13 +34,13 @@
|
||||
"pnp-webpack-plugin": "^1.7.0",
|
||||
"propagating-hammerjs": "^2.0.1",
|
||||
"rails-erb-loader": "^5.5.2",
|
||||
"sass": "^1.54.4",
|
||||
"sass": "^1.54.9",
|
||||
"sass-loader": "^13.0.2",
|
||||
"shakapacker": "6.5.1",
|
||||
"shakapacker": "6.5.2",
|
||||
"sortablejs": "^1.15.0",
|
||||
"sorttable": "^1.0.2",
|
||||
"style-loader": "^3.3.1",
|
||||
"terser-webpack-plugin": "^5.3.5",
|
||||
"terser-webpack-plugin": "^5.3.6",
|
||||
"underscore": "^1.13.4",
|
||||
"uuid": "^8.3.2",
|
||||
"vis-data": "^7.1.4",
|
||||
@ -51,10 +51,11 @@
|
||||
"webpack-cli": "^4.10.0",
|
||||
"webpack-merge": "^5.8.0",
|
||||
"webpack-sources": "^3.2.3",
|
||||
"webpack-subresource-integrity": "^5.1.0",
|
||||
"xss": "^1.0.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"webpack-dev-server": "^4.10.0"
|
||||
"webpack-dev-server": "^4.11.0"
|
||||
},
|
||||
"babel": {
|
||||
"presets": [
|
||||
|
@ -92,7 +92,7 @@ gem install bundler
|
||||
cd /home/vagrant/codeocean
|
||||
|
||||
# config
|
||||
for f in action_mailer.yml database.yml secrets.yml docker.yml.erb mnemosyne.yml
|
||||
for f in action_mailer.yml database.yml secrets.yml docker.yml.erb mnemosyne.yml content_security_policy.yml
|
||||
do
|
||||
if [ ! -f config/$f ]
|
||||
then
|
||||
|
@ -25,6 +25,8 @@ describe FileParameters do
|
||||
|
||||
it 'new file' do
|
||||
submission = create(:submission, exercise: hello_world, id: 1337)
|
||||
controller.instance_variable_set(:@current_user, submission.user)
|
||||
|
||||
new_file = create(:file, context: submission)
|
||||
expect(file_accepted?(new_file)).to be true
|
||||
end
|
||||
@ -42,16 +44,27 @@ describe FileParameters do
|
||||
expect(file_accepted?(hidden_file)).to be false
|
||||
end
|
||||
|
||||
it 'read only file' do
|
||||
it 'read-only file' do
|
||||
read_only_file = create(:file, context: hello_world, read_only: true)
|
||||
expect(file_accepted?(read_only_file)).to be false
|
||||
end
|
||||
|
||||
it 'non existent file' do
|
||||
it 'non-existent file' do
|
||||
# Ensure to use an invalid id for the file.
|
||||
non_existent_file = build(:file, context: hello_world, id: -1)
|
||||
expect(file_accepted?(non_existent_file)).to be false
|
||||
end
|
||||
|
||||
it 'file of another submission' do
|
||||
learner1 = create(:learner)
|
||||
learner2 = create(:learner)
|
||||
submission_learner1 = create(:submission, exercise: hello_world, user: learner1)
|
||||
_submission_learner2 = create(:submission, exercise: hello_world, user: learner2)
|
||||
|
||||
controller.instance_variable_set(:@current_user, learner2)
|
||||
other_submissions_file = create(:file, context: submission_learner1)
|
||||
expect(file_accepted?(other_submissions_file)).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -7,6 +7,26 @@ describe CodeOcean::FilesController do
|
||||
|
||||
before { allow(controller).to receive(:current_user).and_return(user) }
|
||||
|
||||
describe 'GET #show_protected_upload' do
|
||||
context 'with a valid filename' do
|
||||
let(:submission) { create(:submission, exercise: create(:audio_video)) }
|
||||
|
||||
before { get :show_protected_upload, params: {filename: file.name_with_extension, id: file.id} }
|
||||
|
||||
context 'with a binary file' do
|
||||
let(:file) { submission.collect_files.detect {|file| file.file_type.file_extension == '.mp4' } }
|
||||
|
||||
expect_assigns(file: :file)
|
||||
expect_content_type('video/mp4')
|
||||
expect_http_status(:ok)
|
||||
|
||||
it 'sets the correct filename' do
|
||||
expect(response.headers['Content-Disposition']).to include("attachment; filename=\"#{file.name_with_extension}\"")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
let(:submission) { create(:submission, user: user) }
|
||||
|
||||
|
@ -6,7 +6,10 @@ describe ExercisesController do
|
||||
let(:exercise) { create(:dummy) }
|
||||
let(:user) { create(:admin) }
|
||||
|
||||
before { allow(controller).to receive(:current_user).and_return(user) }
|
||||
before do
|
||||
create(:test_file, context: exercise)
|
||||
allow(controller).to receive(:current_user).and_return(user)
|
||||
end
|
||||
|
||||
describe 'PUT #batch_update' do
|
||||
let(:attributes) { {public: 'true'} }
|
||||
@ -184,6 +187,17 @@ describe ExercisesController do
|
||||
expect_flash_message(:alert, :'exercises.implement.no_files')
|
||||
expect_redirect(:exercise)
|
||||
end
|
||||
|
||||
context 'with other users accessing an unpublished exercise' do
|
||||
let(:exercise) { create(:fibonacci, unpublished: true) }
|
||||
let(:user) { create(:teacher) }
|
||||
|
||||
before { perform_request.call }
|
||||
|
||||
expect_assigns(exercise: :exercise)
|
||||
expect_flash_message(:alert, :'exercises.implement.unpublished')
|
||||
expect_redirect(:exercise)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
@ -220,6 +234,8 @@ describe ExercisesController do
|
||||
|
||||
describe 'GET #reload' do
|
||||
context 'when being anyone' do
|
||||
let(:exercise) { create(:fibonacci) }
|
||||
|
||||
before { get :reload, format: :json, params: {id: exercise.id} }
|
||||
|
||||
expect_assigns(exercise: :exercise)
|
||||
|
@ -74,11 +74,9 @@ describe SubmissionsController do
|
||||
|
||||
expect_assigns(file: :file)
|
||||
expect_assigns(submission: :submission)
|
||||
expect_content_type('video/mp4')
|
||||
expect_http_status(:ok)
|
||||
|
||||
it 'sets the correct filename' do
|
||||
expect(response.headers['Content-Disposition']).to include("attachment; filename=\"#{file.name_with_extension}\"")
|
||||
it 'sets the correct redirect' do
|
||||
expect(response.location).to eq protected_upload_url(id: file, filename: file.name_with_extension)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -78,6 +78,25 @@ describe 'Authentication' do
|
||||
expect(page).to have_content(I18n.t('application.not_authorized'))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the authentication token is used to login' do
|
||||
let(:token) { create(:authentication_token, user: user) }
|
||||
|
||||
it 'invalidates the token on login' do
|
||||
mail.deliver_now
|
||||
visit(rfc_link)
|
||||
expect(token.reload.expire_at).to be_within(10.seconds).of(Time.zone.now)
|
||||
end
|
||||
|
||||
it 'does not allow a second login' do
|
||||
mail.deliver_now
|
||||
visit(rfc_link)
|
||||
expect(page).to have_current_path(rfc_link)
|
||||
visit(sign_out_path)
|
||||
visit(rfc_link)
|
||||
expect(page).to have_current_path(root_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -23,6 +23,7 @@ describe 'Editor', js: true do
|
||||
}]
|
||||
end
|
||||
let(:user) { create(:teacher) }
|
||||
let(:exercise_without_test) { create(:tdd) }
|
||||
|
||||
before do
|
||||
visit(sign_in_path)
|
||||
@ -93,12 +94,28 @@ describe 'Editor', js: true do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when an exercise has one or more teacher-defined assessments' do
|
||||
it 'displays the score button' do
|
||||
visit(implement_exercise_path(exercise))
|
||||
expect(page).to have_content(exercise.title)
|
||||
expect(page).to have_content(I18n.t('exercises.editor.score'))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when an exercise has no teacher-defined assessment' do
|
||||
it 'disables the score button' do
|
||||
visit(implement_exercise_path(exercise_without_test))
|
||||
expect(page).to have_content(exercise_without_test.title)
|
||||
expect(page).not_to have_content(I18n.t('exercises.editor.score'))
|
||||
end
|
||||
end
|
||||
|
||||
it 'contains a button for submitting the exercise' do
|
||||
submission = build(:submission, user: user, exercise: exercise)
|
||||
allow(submission).to receive(:calculate_score).and_return(scoring_response)
|
||||
allow(Submission).to receive(:find).and_return(submission)
|
||||
click_button(I18n.t('exercises.editor.score'))
|
||||
expect(page).not_to have_css('#submit_outdated')
|
||||
expect(page).to have_css('#submit')
|
||||
expect(page).not_to have_content(I18n.t('exercises.editor.tooltips.exercise_deadline_passed'))
|
||||
expect(page).to have_content(I18n.t('exercises.editor.submit'))
|
||||
end
|
||||
end
|
||||
|
@ -93,6 +93,25 @@ describe UserMailer do
|
||||
# A five minute tolerance is allowed to account for the time difference between `now` and the creation timestamp of the token.
|
||||
expect(token.expire_at - Time.zone.now).to be_within(5.minutes).of(7.days)
|
||||
end
|
||||
|
||||
it 'sets the correct comment' do
|
||||
expect(mail.body).to include(request_for_comment.comments.first.text)
|
||||
end
|
||||
|
||||
context 'with an HTML comment' do
|
||||
let(:html_comment) { '<b>test</b>' }
|
||||
let(:escaped_comment) { '<b>test</b>' }
|
||||
|
||||
before { request_for_comment.comments.first.update(text: html_comment) }
|
||||
|
||||
it 'does not include the HTML tags' do
|
||||
expect(mail.body).not_to include(html_comment)
|
||||
end
|
||||
|
||||
it 'includes escaped HTML tags' do
|
||||
expect(mail.body).to include(escaped_comment)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#got_new_comment_for_subscription' do
|
||||
@ -128,21 +147,40 @@ describe UserMailer do
|
||||
# A five minute tolerance is allowed to account for the time difference between `now` and the creation timestamp of the token.
|
||||
expect(token.expire_at - Time.zone.now).to be_within(5.minutes).of(7.days)
|
||||
end
|
||||
|
||||
it 'sets the correct comment' do
|
||||
expect(mail.body).to include(request_for_comment.comments.first.text)
|
||||
end
|
||||
|
||||
context 'with an HTML comment' do
|
||||
let(:html_comment) { '<b>test</b>' }
|
||||
let(:escaped_comment) { '<b>test</b>' }
|
||||
|
||||
before { request_for_comment.comments.first.update(text: html_comment) }
|
||||
|
||||
it 'does not include the HTML tags' do
|
||||
expect(mail.body).not_to include(html_comment)
|
||||
end
|
||||
|
||||
it 'includes escaped HTML tags' do
|
||||
expect(mail.body).to include(escaped_comment)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#send_thank_you_note' do
|
||||
let(:user) { create(:learner) }
|
||||
let(:token) { AuthenticationToken.find_by(user: user) }
|
||||
let(:request_for_comments) { create(:rfc_with_comment, user: user) }
|
||||
let(:receiver) { InternalUser.create(attributes_for(:teacher)) }
|
||||
let(:mail) { described_class.send_thank_you_note(request_for_comments, receiver).deliver_now }
|
||||
let(:receiver) { create(:teacher) }
|
||||
let(:token) { AuthenticationToken.find_by(user: receiver) }
|
||||
let(:request_for_comment) { create(:rfc_with_comment, user: user) }
|
||||
let(:mail) { described_class.send_thank_you_note(request_for_comment, receiver).deliver_now }
|
||||
|
||||
it 'sets the correct sender' do
|
||||
expect(mail.from).to include('codeocean@hpi.de')
|
||||
end
|
||||
|
||||
it 'sets the correct subject' do
|
||||
expect(mail.subject).to eq(I18n.t('mailers.user_mailer.send_thank_you_note.subject', author: request_for_comments.user.displayname))
|
||||
expect(mail.subject).to eq(I18n.t('mailers.user_mailer.send_thank_you_note.subject', author: request_for_comment.user.displayname))
|
||||
end
|
||||
|
||||
it 'sets the correct receiver' do
|
||||
@ -150,7 +188,7 @@ describe UserMailer do
|
||||
end
|
||||
|
||||
it 'includes the correct URL' do
|
||||
expect(mail.body).to include(request_for_comment_url(request_for_comments, token: token.shared_secret))
|
||||
expect(mail.body).to include(request_for_comment_url(request_for_comment, token: token.shared_secret))
|
||||
end
|
||||
|
||||
it 'creates a new authentication token' do
|
||||
@ -162,5 +200,24 @@ describe UserMailer do
|
||||
# A five minute tolerance is allowed to account for the time difference between `now` and the creation timestamp of the token.
|
||||
expect(token.expire_at - Time.zone.now).to be_within(5.minutes).of(7.days)
|
||||
end
|
||||
|
||||
it 'sets the correct thank_you_note' do
|
||||
expect(mail.body).to include(request_for_comment.thank_you_note)
|
||||
end
|
||||
|
||||
context 'with an HTML comment' do
|
||||
let(:html_comment) { '<b>test</b>' }
|
||||
let(:escaped_comment) { '<b>test</b>' }
|
||||
|
||||
before { request_for_comment.update(thank_you_note: html_comment) }
|
||||
|
||||
it 'does not include the HTML tags' do
|
||||
expect(mail.body).not_to include(html_comment)
|
||||
end
|
||||
|
||||
it 'includes escaped HTML tags' do
|
||||
expect(mail.body).to include(escaped_comment)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -69,7 +69,27 @@ describe CodeOcean::File do
|
||||
end
|
||||
|
||||
context 'when the path has been modified' do
|
||||
before { file.update(native_file: '../../../../secrets.yml') }
|
||||
before do
|
||||
file.update_column(:native_file, '../../../../secrets.yml') # rubocop:disable Rails/SkipsModelValidations
|
||||
file.reload
|
||||
end
|
||||
|
||||
it 'does not read the native file' do
|
||||
expect(file.read).not_to be_present
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a symlink is used' do
|
||||
let(:fake_upload_location) { File.join(CarrierWave::Uploader::Base.new.root, 'uploads', 'files', 'secrets.yml') }
|
||||
|
||||
before do
|
||||
FileUtils.touch Rails.root.join('config/secrets.yml')
|
||||
File.symlink Rails.root.join('config/secrets.yml'), fake_upload_location
|
||||
file.update_column(:native_file, '../secrets.yml') # rubocop:disable Rails/SkipsModelValidations
|
||||
file.reload
|
||||
end
|
||||
|
||||
after { File.delete(fake_upload_location) }
|
||||
|
||||
it 'does not read the native file' do
|
||||
expect(file.read).not_to be_present
|
||||
|
@ -124,4 +124,41 @@ describe Exercise do
|
||||
expect(exercise.duplicate).to be_a(described_class)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#teacher_defined_assessment?' do
|
||||
let(:exercise) { create(:dummy) }
|
||||
|
||||
context 'when no assessment is defined' do
|
||||
it 'returns false' do
|
||||
expect(exercise).not_to be_teacher_defined_assessment
|
||||
end
|
||||
end
|
||||
|
||||
context 'when unit tests are defined' do
|
||||
before { create(:test_file, context: exercise) }
|
||||
|
||||
it 'returns true' do
|
||||
expect(exercise).to be_teacher_defined_assessment
|
||||
end
|
||||
end
|
||||
|
||||
context 'when linter tests are defined' do
|
||||
before { create(:test_file, context: exercise, role: 'teacher_defined_linter') }
|
||||
|
||||
it 'returns true' do
|
||||
expect(exercise).to be_teacher_defined_assessment
|
||||
end
|
||||
end
|
||||
|
||||
context 'when unit and linter tests are defined' do
|
||||
before do
|
||||
create(:test_file, context: exercise)
|
||||
create(:test_file, context: exercise, role: 'teacher_defined_linter')
|
||||
end
|
||||
|
||||
it 'returns true' do
|
||||
expect(exercise).to be_teacher_defined_assessment
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -54,7 +54,7 @@ describe CodeharborLinkPolicy do
|
||||
end
|
||||
end
|
||||
|
||||
permissions(:enabled?) do
|
||||
permissions :enabled? do
|
||||
it 'reflects the config option' do
|
||||
%i[external_user admin teacher].each do |factory_name|
|
||||
expect(policy).to permit(create(factory_name), codeharbor_link)
|
||||
@ -72,7 +72,7 @@ describe CodeharborLinkPolicy do
|
||||
allow(codeocean_config).to receive(:read).and_return(codeharbor_config)
|
||||
end
|
||||
|
||||
permissions(:enabled?) do
|
||||
permissions :enabled? do
|
||||
it 'reflects the config option' do
|
||||
%i[external_user admin teacher].each do |factory_name|
|
||||
expect(policy).not_to permit(create(factory_name), codeharbor_link)
|
||||
|
@ -7,19 +7,17 @@ describe ExecutionEnvironmentPolicy do
|
||||
|
||||
let(:execution_environment) { build(:ruby) }
|
||||
|
||||
[:index?].each do |action|
|
||||
permissions(action) do
|
||||
it 'grants access to admins' do
|
||||
expect(policy).to permit(build(:admin), execution_environment)
|
||||
end
|
||||
permissions :index? do
|
||||
it 'grants access to admins' do
|
||||
expect(policy).to permit(build(:admin), execution_environment)
|
||||
end
|
||||
|
||||
it 'grants access to teachers' do
|
||||
expect(policy).to permit(build(:teacher), execution_environment)
|
||||
end
|
||||
it 'grants access to teachers' do
|
||||
expect(policy).to permit(build(:teacher), execution_environment)
|
||||
end
|
||||
|
||||
it 'does not grant access to external users' do
|
||||
expect(policy).not_to permit(build(:external_user), execution_environment)
|
||||
end
|
||||
it 'does not grant access to external users' do
|
||||
expect(policy).not_to permit(build(:external_user), execution_environment)
|
||||
end
|
||||
end
|
||||
|
||||
@ -59,7 +57,7 @@ describe ExecutionEnvironmentPolicy do
|
||||
end
|
||||
end
|
||||
|
||||
permissions(:sync_all_to_runner_management?) do
|
||||
permissions :sync_all_to_runner_management? do
|
||||
it 'grants access to the admin' do
|
||||
expect(policy).to permit(build(:admin))
|
||||
end
|
||||
|
@ -104,19 +104,79 @@ describe ExercisePolicy do
|
||||
end
|
||||
end
|
||||
|
||||
[:show?].each do |action|
|
||||
permissions :show? do
|
||||
it 'not grants access to external users' do
|
||||
expect(policy).not_to permit(build(:external_user), exercise)
|
||||
end
|
||||
end
|
||||
|
||||
%i[implement? working_times? intervention? search? reload?].each do |action|
|
||||
permissions(action) do
|
||||
it 'not grants access to external users' do
|
||||
expect(policy).not_to permit(build(:external_user), exercise)
|
||||
context 'when the exercise has no visible files' do
|
||||
let(:exercise) { create(:dummy) }
|
||||
|
||||
it 'does not grant access to anyone' do
|
||||
%i[admin external_user teacher].each do |factory_name|
|
||||
expect(policy).not_to permit(build(factory_name), exercise)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the exercise has visible files' do
|
||||
let(:exercise) { create(:fibonacci) }
|
||||
|
||||
it 'grants access to anyone' do
|
||||
%i[admin external_user teacher].each do |factory_name|
|
||||
expect(policy).to permit(build(factory_name), exercise)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the exercise is published' do
|
||||
let(:exercise) { create(:fibonacci, unpublished: false) }
|
||||
|
||||
it 'grants access to anyone' do
|
||||
%i[admin external_user teacher].each do |factory_name|
|
||||
expect(policy).to permit(build(factory_name), exercise)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the exercise is unpublished' do
|
||||
let(:exercise) { create(:fibonacci, unpublished: true) }
|
||||
|
||||
it 'grants access to admins' do
|
||||
expect(policy).to permit(build(:admin), exercise)
|
||||
end
|
||||
|
||||
it 'grants access to the author' do
|
||||
expect(policy).to permit(exercise.author, exercise)
|
||||
end
|
||||
|
||||
it 'does not grant access to everyone' do
|
||||
%i[external_user teacher].each do |factory_name|
|
||||
expect(policy).not_to permit(build(factory_name), exercise)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
%i[implement? submit?].each do |action|
|
||||
permissions(action) do
|
||||
permissions :submit? do
|
||||
context 'when teacher-defined assessments are available' do
|
||||
before { create(:test_file, context: exercise) }
|
||||
|
||||
it 'grants access to anyone' do
|
||||
%i[admin external_user teacher].each do |factory_name|
|
||||
expect(policy).to permit(build(factory_name), Exercise.new)
|
||||
expect(policy).to permit(build(factory_name), exercise)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when teacher-defined assessments are not available' do
|
||||
it 'does not grant access to anyone' do
|
||||
%i[admin external_user teacher].each do |factory_name|
|
||||
expect(policy).not_to permit(build(factory_name), exercise)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -16,14 +16,12 @@ describe ExternalUserPolicy do
|
||||
end
|
||||
end
|
||||
|
||||
[:index?].each do |action|
|
||||
permissions(action) do
|
||||
it 'grants access to admins and teachers only' do
|
||||
expect(policy).to permit(build(:admin), ExternalUser.new)
|
||||
expect(policy).to permit(build(:teacher), ExternalUser.new)
|
||||
[:external_user].each do |factory_name|
|
||||
expect(policy).not_to permit(build(factory_name), ExternalUser.new)
|
||||
end
|
||||
permissions :index? do
|
||||
it 'grants access to admins and teachers only' do
|
||||
expect(policy).to permit(build(:admin), ExternalUser.new)
|
||||
expect(policy).to permit(build(:teacher), ExternalUser.new)
|
||||
[:external_user].each do |factory_name|
|
||||
expect(policy).not_to permit(build(factory_name), ExternalUser.new)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
467
yarn.lock
467
yarn.lock
@ -17,38 +17,38 @@
|
||||
dependencies:
|
||||
"@babel/highlight" "^7.18.6"
|
||||
|
||||
"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8":
|
||||
version "7.18.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d"
|
||||
integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==
|
||||
"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8", "@babel/compat-data@^7.19.0":
|
||||
version "7.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.0.tgz#2a592fd89bacb1fcde68de31bee4f2f2dacb0e86"
|
||||
integrity sha512-y5rqgTTPTmaF5e2nVhOxw+Ur9HDJLsWb6U/KpgUzRZEdPfE6VOubXBKLdbcUTijzRptednSBDQbYZBOSqJxpJw==
|
||||
|
||||
"@babel/core@^7.18.10":
|
||||
version "7.18.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.10.tgz#39ad504991d77f1f3da91be0b8b949a5bc466fb8"
|
||||
integrity sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==
|
||||
"@babel/core@^7.19.0":
|
||||
version "7.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.19.0.tgz#d2f5f4f2033c00de8096be3c9f45772563e150c3"
|
||||
integrity sha512-reM4+U7B9ss148rh2n1Qs9ASS+w94irYXga7c2jaQv9RVzpS7Mv1a9rnYYwuDa45G+DkORt9g6An2k/V4d9LbQ==
|
||||
dependencies:
|
||||
"@ampproject/remapping" "^2.1.0"
|
||||
"@babel/code-frame" "^7.18.6"
|
||||
"@babel/generator" "^7.18.10"
|
||||
"@babel/helper-compilation-targets" "^7.18.9"
|
||||
"@babel/helper-module-transforms" "^7.18.9"
|
||||
"@babel/helpers" "^7.18.9"
|
||||
"@babel/parser" "^7.18.10"
|
||||
"@babel/generator" "^7.19.0"
|
||||
"@babel/helper-compilation-targets" "^7.19.0"
|
||||
"@babel/helper-module-transforms" "^7.19.0"
|
||||
"@babel/helpers" "^7.19.0"
|
||||
"@babel/parser" "^7.19.0"
|
||||
"@babel/template" "^7.18.10"
|
||||
"@babel/traverse" "^7.18.10"
|
||||
"@babel/types" "^7.18.10"
|
||||
"@babel/traverse" "^7.19.0"
|
||||
"@babel/types" "^7.19.0"
|
||||
convert-source-map "^1.7.0"
|
||||
debug "^4.1.0"
|
||||
gensync "^1.0.0-beta.2"
|
||||
json5 "^2.2.1"
|
||||
semver "^6.3.0"
|
||||
|
||||
"@babel/generator@^7.18.10":
|
||||
version "7.18.12"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.12.tgz#fa58daa303757bd6f5e4bbca91b342040463d9f4"
|
||||
integrity sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==
|
||||
"@babel/generator@^7.19.0":
|
||||
version "7.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.19.0.tgz#785596c06425e59334df2ccee63ab166b738419a"
|
||||
integrity sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg==
|
||||
dependencies:
|
||||
"@babel/types" "^7.18.10"
|
||||
"@babel/types" "^7.19.0"
|
||||
"@jridgewell/gen-mapping" "^0.3.2"
|
||||
jsesc "^2.5.1"
|
||||
|
||||
@ -67,33 +67,33 @@
|
||||
"@babel/helper-explode-assignable-expression" "^7.18.6"
|
||||
"@babel/types" "^7.18.9"
|
||||
|
||||
"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz#69e64f57b524cde3e5ff6cc5a9f4a387ee5563bf"
|
||||
integrity sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==
|
||||
"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.19.0":
|
||||
version "7.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.0.tgz#537ec8339d53e806ed422f1e06c8f17d55b96bb0"
|
||||
integrity sha512-Ai5bNWXIvwDvWM7njqsG3feMlL9hCVQsPYXodsZyLwshYkZVJt59Gftau4VrE8S9IT9asd2uSP1hG6wCNw+sXA==
|
||||
dependencies:
|
||||
"@babel/compat-data" "^7.18.8"
|
||||
"@babel/compat-data" "^7.19.0"
|
||||
"@babel/helper-validator-option" "^7.18.6"
|
||||
browserslist "^4.20.2"
|
||||
semver "^6.3.0"
|
||||
|
||||
"@babel/helper-create-class-features-plugin@^7.18.6":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz#d802ee16a64a9e824fcbf0a2ffc92f19d58550ce"
|
||||
integrity sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw==
|
||||
version "7.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.19.0.tgz#bfd6904620df4e46470bae4850d66be1054c404b"
|
||||
integrity sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw==
|
||||
dependencies:
|
||||
"@babel/helper-annotate-as-pure" "^7.18.6"
|
||||
"@babel/helper-environment-visitor" "^7.18.9"
|
||||
"@babel/helper-function-name" "^7.18.9"
|
||||
"@babel/helper-function-name" "^7.19.0"
|
||||
"@babel/helper-member-expression-to-functions" "^7.18.9"
|
||||
"@babel/helper-optimise-call-expression" "^7.18.6"
|
||||
"@babel/helper-replace-supers" "^7.18.9"
|
||||
"@babel/helper-split-export-declaration" "^7.18.6"
|
||||
|
||||
"@babel/helper-create-regexp-features-plugin@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz#3e35f4e04acbbf25f1b3534a657610a000543d3c"
|
||||
integrity sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A==
|
||||
"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.19.0":
|
||||
version "7.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz#7976aca61c0984202baca73d84e2337a5424a41b"
|
||||
integrity sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw==
|
||||
dependencies:
|
||||
"@babel/helper-annotate-as-pure" "^7.18.6"
|
||||
regexpu-core "^5.1.0"
|
||||
@ -122,13 +122,13 @@
|
||||
dependencies:
|
||||
"@babel/types" "^7.18.6"
|
||||
|
||||
"@babel/helper-function-name@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz#940e6084a55dee867d33b4e487da2676365e86b0"
|
||||
integrity sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==
|
||||
"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0":
|
||||
version "7.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c"
|
||||
integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==
|
||||
dependencies:
|
||||
"@babel/template" "^7.18.6"
|
||||
"@babel/types" "^7.18.9"
|
||||
"@babel/template" "^7.18.10"
|
||||
"@babel/types" "^7.19.0"
|
||||
|
||||
"@babel/helper-hoist-variables@^7.18.6":
|
||||
version "7.18.6"
|
||||
@ -151,19 +151,19 @@
|
||||
dependencies:
|
||||
"@babel/types" "^7.18.6"
|
||||
|
||||
"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz#5a1079c005135ed627442df31a42887e80fcb712"
|
||||
integrity sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==
|
||||
"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.19.0":
|
||||
version "7.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz#309b230f04e22c58c6a2c0c0c7e50b216d350c30"
|
||||
integrity sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==
|
||||
dependencies:
|
||||
"@babel/helper-environment-visitor" "^7.18.9"
|
||||
"@babel/helper-module-imports" "^7.18.6"
|
||||
"@babel/helper-simple-access" "^7.18.6"
|
||||
"@babel/helper-split-export-declaration" "^7.18.6"
|
||||
"@babel/helper-validator-identifier" "^7.18.6"
|
||||
"@babel/template" "^7.18.6"
|
||||
"@babel/traverse" "^7.18.9"
|
||||
"@babel/types" "^7.18.9"
|
||||
"@babel/template" "^7.18.10"
|
||||
"@babel/traverse" "^7.19.0"
|
||||
"@babel/types" "^7.19.0"
|
||||
|
||||
"@babel/helper-optimise-call-expression@^7.18.6":
|
||||
version "7.18.6"
|
||||
@ -172,10 +172,10 @@
|
||||
dependencies:
|
||||
"@babel/types" "^7.18.6"
|
||||
|
||||
"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f"
|
||||
integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==
|
||||
"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
|
||||
version "7.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz#4796bb14961521f0f8715990bee2fb6e51ce21bf"
|
||||
integrity sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==
|
||||
|
||||
"@babel/helper-remap-async-to-generator@^7.18.6", "@babel/helper-remap-async-to-generator@^7.18.9":
|
||||
version "7.18.9"
|
||||
@ -235,23 +235,23 @@
|
||||
integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==
|
||||
|
||||
"@babel/helper-wrap-function@^7.18.9":
|
||||
version "7.18.11"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.18.11.tgz#bff23ace436e3f6aefb61f85ffae2291c80ed1fb"
|
||||
integrity sha512-oBUlbv+rjZLh2Ks9SKi4aL7eKaAXBWleHzU89mP0G6BMUlRxSckk9tSIkgDGydhgFxHuGSlBQZfnaD47oBEB7w==
|
||||
version "7.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.19.0.tgz#89f18335cff1152373222f76a4b37799636ae8b1"
|
||||
integrity sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg==
|
||||
dependencies:
|
||||
"@babel/helper-function-name" "^7.18.9"
|
||||
"@babel/helper-function-name" "^7.19.0"
|
||||
"@babel/template" "^7.18.10"
|
||||
"@babel/traverse" "^7.18.11"
|
||||
"@babel/types" "^7.18.10"
|
||||
"@babel/traverse" "^7.19.0"
|
||||
"@babel/types" "^7.19.0"
|
||||
|
||||
"@babel/helpers@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.9.tgz#4bef3b893f253a1eced04516824ede94dcfe7ff9"
|
||||
integrity sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==
|
||||
"@babel/helpers@^7.19.0":
|
||||
version "7.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.19.0.tgz#f30534657faf246ae96551d88dd31e9d1fa1fc18"
|
||||
integrity sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg==
|
||||
dependencies:
|
||||
"@babel/template" "^7.18.6"
|
||||
"@babel/traverse" "^7.18.9"
|
||||
"@babel/types" "^7.18.9"
|
||||
"@babel/template" "^7.18.10"
|
||||
"@babel/traverse" "^7.19.0"
|
||||
"@babel/types" "^7.19.0"
|
||||
|
||||
"@babel/highlight@^7.18.6":
|
||||
version "7.18.6"
|
||||
@ -262,10 +262,10 @@
|
||||
chalk "^2.0.0"
|
||||
js-tokens "^4.0.0"
|
||||
|
||||
"@babel/parser@^7.18.10", "@babel/parser@^7.18.11":
|
||||
version "7.18.11"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.11.tgz#68bb07ab3d380affa9a3f96728df07969645d2d9"
|
||||
integrity sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==
|
||||
"@babel/parser@^7.18.10", "@babel/parser@^7.19.0":
|
||||
version "7.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.0.tgz#497fcafb1d5b61376959c1c338745ef0577aa02c"
|
||||
integrity sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw==
|
||||
|
||||
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6":
|
||||
version "7.18.6"
|
||||
@ -283,13 +283,13 @@
|
||||
"@babel/helper-skip-transparent-expression-wrappers" "^7.18.9"
|
||||
"@babel/plugin-proposal-optional-chaining" "^7.18.9"
|
||||
|
||||
"@babel/plugin-proposal-async-generator-functions@^7.18.10":
|
||||
version "7.18.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz#85ea478c98b0095c3e4102bff3b67d306ed24952"
|
||||
integrity sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew==
|
||||
"@babel/plugin-proposal-async-generator-functions@^7.19.0":
|
||||
version "7.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.19.0.tgz#cf5740194f170467df20581712400487efc79ff1"
|
||||
integrity sha512-nhEByMUTx3uZueJ/QkJuSlCfN4FGg+xy+vRsfGQGzSauq5ks2Deid2+05Q3KhfaUjvec1IGhw/Zm3cFm8JigTQ==
|
||||
dependencies:
|
||||
"@babel/helper-environment-visitor" "^7.18.9"
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
"@babel/helper-plugin-utils" "^7.19.0"
|
||||
"@babel/helper-remap-async-to-generator" "^7.18.9"
|
||||
"@babel/plugin-syntax-async-generators" "^7.8.4"
|
||||
|
||||
@ -547,16 +547,17 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
|
||||
"@babel/plugin-transform-classes@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.9.tgz#90818efc5b9746879b869d5ce83eb2aa48bbc3da"
|
||||
integrity sha512-EkRQxsxoytpTlKJmSPYrsOMjCILacAjtSVkd4gChEe2kXjFCun3yohhW5I7plXJhCemM0gKsaGMcO8tinvCA5g==
|
||||
"@babel/plugin-transform-classes@^7.19.0":
|
||||
version "7.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz#0e61ec257fba409c41372175e7c1e606dc79bb20"
|
||||
integrity sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A==
|
||||
dependencies:
|
||||
"@babel/helper-annotate-as-pure" "^7.18.6"
|
||||
"@babel/helper-compilation-targets" "^7.19.0"
|
||||
"@babel/helper-environment-visitor" "^7.18.9"
|
||||
"@babel/helper-function-name" "^7.18.9"
|
||||
"@babel/helper-function-name" "^7.19.0"
|
||||
"@babel/helper-optimise-call-expression" "^7.18.6"
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
"@babel/helper-plugin-utils" "^7.19.0"
|
||||
"@babel/helper-replace-supers" "^7.18.9"
|
||||
"@babel/helper-split-export-declaration" "^7.18.6"
|
||||
globals "^11.1.0"
|
||||
@ -568,10 +569,10 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
|
||||
"@babel/plugin-transform-destructuring@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.9.tgz#68906549c021cb231bee1db21d3b5b095f8ee292"
|
||||
integrity sha512-p5VCYNddPLkZTq4XymQIaIfZNJwT9YsjkPOhkVEqt6QIpQFZVM9IltqqYpOEkJoN1DPznmxUDyZ5CTZs/ZCuHA==
|
||||
"@babel/plugin-transform-destructuring@^7.18.13":
|
||||
version "7.18.13"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.13.tgz#9e03bc4a94475d62b7f4114938e6c5c33372cbf5"
|
||||
integrity sha512-TodpQ29XekIsex2A+YJPj5ax2plkGa8YYY6mFjCohk/IG9IY42Rtuj1FuDeemfg2ipxIFLzPeA83SIBnlhSIow==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
|
||||
@ -647,14 +648,14 @@
|
||||
"@babel/helper-simple-access" "^7.18.6"
|
||||
babel-plugin-dynamic-import-node "^2.3.3"
|
||||
|
||||
"@babel/plugin-transform-modules-systemjs@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.9.tgz#545df284a7ac6a05125e3e405e536c5853099a06"
|
||||
integrity sha512-zY/VSIbbqtoRoJKo2cDTewL364jSlZGvn0LKOf9ntbfxOvjfmyrdtEEOAdswOswhZEb8UH3jDkCKHd1sPgsS0A==
|
||||
"@babel/plugin-transform-modules-systemjs@^7.19.0":
|
||||
version "7.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.0.tgz#5f20b471284430f02d9c5059d9b9a16d4b085a1f"
|
||||
integrity sha512-x9aiR0WXAWmOWsqcsnrzGR+ieaTMVyGyffPVA7F8cXAGt/UxefYv6uSHZLkAFChN5M5Iy1+wjE+xJuPt22H39A==
|
||||
dependencies:
|
||||
"@babel/helper-hoist-variables" "^7.18.6"
|
||||
"@babel/helper-module-transforms" "^7.18.9"
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
"@babel/helper-module-transforms" "^7.19.0"
|
||||
"@babel/helper-plugin-utils" "^7.19.0"
|
||||
"@babel/helper-validator-identifier" "^7.18.6"
|
||||
babel-plugin-dynamic-import-node "^2.3.3"
|
||||
|
||||
@ -666,13 +667,13 @@
|
||||
"@babel/helper-module-transforms" "^7.18.6"
|
||||
"@babel/helper-plugin-utils" "^7.18.6"
|
||||
|
||||
"@babel/plugin-transform-named-capturing-groups-regex@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.18.6.tgz#c89bfbc7cc6805d692f3a49bc5fc1b630007246d"
|
||||
integrity sha512-UmEOGF8XgaIqD74bC8g7iV3RYj8lMf0Bw7NJzvnS9qQhM4mg+1WHKotUIdjxgD2RGrgFLZZPCFPFj3P/kVDYhg==
|
||||
"@babel/plugin-transform-named-capturing-groups-regex@^7.19.0":
|
||||
version "7.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.0.tgz#58c52422e4f91a381727faed7d513c89d7f41ada"
|
||||
integrity sha512-HDSuqOQzkU//kfGdiHBt71/hkDTApw4U/cMVgKgX7PqfB3LOaK+2GtCEsBu1dL9CkswDm0Gwehht1dCr421ULQ==
|
||||
dependencies:
|
||||
"@babel/helper-create-regexp-features-plugin" "^7.18.6"
|
||||
"@babel/helper-plugin-utils" "^7.18.6"
|
||||
"@babel/helper-create-regexp-features-plugin" "^7.19.0"
|
||||
"@babel/helper-plugin-utils" "^7.19.0"
|
||||
|
||||
"@babel/plugin-transform-new-target@^7.18.6":
|
||||
version "7.18.6"
|
||||
@ -737,12 +738,12 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.18.6"
|
||||
|
||||
"@babel/plugin-transform-spread@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.9.tgz#6ea7a6297740f381c540ac56caf75b05b74fb664"
|
||||
integrity sha512-39Q814wyoOPtIB/qGopNIL9xDChOE1pNU0ZY5dO0owhiVt/5kFm4li+/bBtwc7QotG0u5EPzqhZdjMtmqBqyQA==
|
||||
"@babel/plugin-transform-spread@^7.19.0":
|
||||
version "7.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz#dd60b4620c2fec806d60cfaae364ec2188d593b6"
|
||||
integrity sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
"@babel/helper-plugin-utils" "^7.19.0"
|
||||
"@babel/helper-skip-transparent-expression-wrappers" "^7.18.9"
|
||||
|
||||
"@babel/plugin-transform-sticky-regex@^7.18.6":
|
||||
@ -782,17 +783,17 @@
|
||||
"@babel/helper-plugin-utils" "^7.18.6"
|
||||
|
||||
"@babel/preset-env@7":
|
||||
version "7.18.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.18.10.tgz#83b8dfe70d7eea1aae5a10635ab0a5fe60dfc0f4"
|
||||
integrity sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA==
|
||||
version "7.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.19.0.tgz#fd18caf499a67d6411b9ded68dc70d01ed1e5da7"
|
||||
integrity sha512-1YUju1TAFuzjIQqNM9WsF4U6VbD/8t3wEAlw3LFYuuEr+ywqLRcSXxFKz4DCEj+sN94l/XTDiUXYRrsvMpz9WQ==
|
||||
dependencies:
|
||||
"@babel/compat-data" "^7.18.8"
|
||||
"@babel/helper-compilation-targets" "^7.18.9"
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
"@babel/compat-data" "^7.19.0"
|
||||
"@babel/helper-compilation-targets" "^7.19.0"
|
||||
"@babel/helper-plugin-utils" "^7.19.0"
|
||||
"@babel/helper-validator-option" "^7.18.6"
|
||||
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6"
|
||||
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.9"
|
||||
"@babel/plugin-proposal-async-generator-functions" "^7.18.10"
|
||||
"@babel/plugin-proposal-async-generator-functions" "^7.19.0"
|
||||
"@babel/plugin-proposal-class-properties" "^7.18.6"
|
||||
"@babel/plugin-proposal-class-static-block" "^7.18.6"
|
||||
"@babel/plugin-proposal-dynamic-import" "^7.18.6"
|
||||
@ -826,9 +827,9 @@
|
||||
"@babel/plugin-transform-async-to-generator" "^7.18.6"
|
||||
"@babel/plugin-transform-block-scoped-functions" "^7.18.6"
|
||||
"@babel/plugin-transform-block-scoping" "^7.18.9"
|
||||
"@babel/plugin-transform-classes" "^7.18.9"
|
||||
"@babel/plugin-transform-classes" "^7.19.0"
|
||||
"@babel/plugin-transform-computed-properties" "^7.18.9"
|
||||
"@babel/plugin-transform-destructuring" "^7.18.9"
|
||||
"@babel/plugin-transform-destructuring" "^7.18.13"
|
||||
"@babel/plugin-transform-dotall-regex" "^7.18.6"
|
||||
"@babel/plugin-transform-duplicate-keys" "^7.18.9"
|
||||
"@babel/plugin-transform-exponentiation-operator" "^7.18.6"
|
||||
@ -838,9 +839,9 @@
|
||||
"@babel/plugin-transform-member-expression-literals" "^7.18.6"
|
||||
"@babel/plugin-transform-modules-amd" "^7.18.6"
|
||||
"@babel/plugin-transform-modules-commonjs" "^7.18.6"
|
||||
"@babel/plugin-transform-modules-systemjs" "^7.18.9"
|
||||
"@babel/plugin-transform-modules-systemjs" "^7.19.0"
|
||||
"@babel/plugin-transform-modules-umd" "^7.18.6"
|
||||
"@babel/plugin-transform-named-capturing-groups-regex" "^7.18.6"
|
||||
"@babel/plugin-transform-named-capturing-groups-regex" "^7.19.0"
|
||||
"@babel/plugin-transform-new-target" "^7.18.6"
|
||||
"@babel/plugin-transform-object-super" "^7.18.6"
|
||||
"@babel/plugin-transform-parameters" "^7.18.8"
|
||||
@ -848,14 +849,14 @@
|
||||
"@babel/plugin-transform-regenerator" "^7.18.6"
|
||||
"@babel/plugin-transform-reserved-words" "^7.18.6"
|
||||
"@babel/plugin-transform-shorthand-properties" "^7.18.6"
|
||||
"@babel/plugin-transform-spread" "^7.18.9"
|
||||
"@babel/plugin-transform-spread" "^7.19.0"
|
||||
"@babel/plugin-transform-sticky-regex" "^7.18.6"
|
||||
"@babel/plugin-transform-template-literals" "^7.18.9"
|
||||
"@babel/plugin-transform-typeof-symbol" "^7.18.9"
|
||||
"@babel/plugin-transform-unicode-escapes" "^7.18.10"
|
||||
"@babel/plugin-transform-unicode-regex" "^7.18.6"
|
||||
"@babel/preset-modules" "^0.1.5"
|
||||
"@babel/types" "^7.18.10"
|
||||
"@babel/types" "^7.19.0"
|
||||
babel-plugin-polyfill-corejs2 "^0.3.2"
|
||||
babel-plugin-polyfill-corejs3 "^0.5.3"
|
||||
babel-plugin-polyfill-regenerator "^0.4.0"
|
||||
@ -874,13 +875,13 @@
|
||||
esutils "^2.0.2"
|
||||
|
||||
"@babel/runtime@7", "@babel/runtime@^7.8.4":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a"
|
||||
integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==
|
||||
version "7.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259"
|
||||
integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/template@^7.18.10", "@babel/template@^7.18.6":
|
||||
"@babel/template@^7.18.10":
|
||||
version "7.18.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71"
|
||||
integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==
|
||||
@ -889,26 +890,26 @@
|
||||
"@babel/parser" "^7.18.10"
|
||||
"@babel/types" "^7.18.10"
|
||||
|
||||
"@babel/traverse@^7.18.10", "@babel/traverse@^7.18.11", "@babel/traverse@^7.18.9":
|
||||
version "7.18.11"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.11.tgz#3d51f2afbd83ecf9912bcbb5c4d94e3d2ddaa16f"
|
||||
integrity sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==
|
||||
"@babel/traverse@^7.18.9", "@babel/traverse@^7.19.0":
|
||||
version "7.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.0.tgz#eb9c561c7360005c592cc645abafe0c3c4548eed"
|
||||
integrity sha512-4pKpFRDh+utd2mbRC8JLnlsMUii3PMHjpL6a0SZ4NMZy7YFP9aXORxEhdMVOc9CpWtDF09IkciQLEhK7Ml7gRA==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.18.6"
|
||||
"@babel/generator" "^7.18.10"
|
||||
"@babel/generator" "^7.19.0"
|
||||
"@babel/helper-environment-visitor" "^7.18.9"
|
||||
"@babel/helper-function-name" "^7.18.9"
|
||||
"@babel/helper-function-name" "^7.19.0"
|
||||
"@babel/helper-hoist-variables" "^7.18.6"
|
||||
"@babel/helper-split-export-declaration" "^7.18.6"
|
||||
"@babel/parser" "^7.18.11"
|
||||
"@babel/types" "^7.18.10"
|
||||
"@babel/parser" "^7.19.0"
|
||||
"@babel/types" "^7.19.0"
|
||||
debug "^4.1.0"
|
||||
globals "^11.1.0"
|
||||
|
||||
"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.4.4":
|
||||
version "7.18.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.10.tgz#4908e81b6b339ca7c6b7a555a5fc29446f26dde6"
|
||||
integrity sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==
|
||||
"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.4.4":
|
||||
version "7.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.0.tgz#75f21d73d73dc0351f3368d28db73465f4814600"
|
||||
integrity sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==
|
||||
dependencies:
|
||||
"@babel/helper-string-parser" "^7.18.10"
|
||||
"@babel/helper-validator-identifier" "^7.18.6"
|
||||
@ -926,10 +927,10 @@
|
||||
dependencies:
|
||||
"@types/hammerjs" "^2.0.36"
|
||||
|
||||
"@fortawesome/fontawesome-free@^6.1.2":
|
||||
version "6.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-6.1.2.tgz#d18880eddeadd42b1c64cb559f2f3d13d47a4a64"
|
||||
integrity sha512-XwWADtfdSN73/udaFm+1mnGIj/ShDZNFMe/PRoqv3FhQ4GNI2PUN70yFTPsjq65Lw2C9i4TG5/hTbxXIXVCiqQ==
|
||||
"@fortawesome/fontawesome-free@^6.2.0":
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-6.2.0.tgz#ba3510825b332816fe7190f28827f8cb33a298b5"
|
||||
integrity sha512-CNR7qRIfCwWHNN7FnKUniva94edPdyQzil/zCwk3v6k4R6rR2Fr8i4s3PM7n/lyfPA6Zfko9z5WDzFxG9SW1uQ==
|
||||
|
||||
"@jridgewell/gen-mapping@^0.1.0":
|
||||
version "0.1.1"
|
||||
@ -989,56 +990,46 @@
|
||||
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45"
|
||||
integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==
|
||||
|
||||
"@sentry/browser@^6.11.0":
|
||||
version "6.19.7"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-6.19.7.tgz#a40b6b72d911b5f1ed70ed3b4e7d4d4e625c0b5f"
|
||||
integrity sha512-oDbklp4O3MtAM4mtuwyZLrgO1qDVYIujzNJQzXmi9YzymJCuzMLSRDvhY83NNDCRxf0pds4DShgYeZdbSyKraA==
|
||||
"@sentry/browser@^7.11.1":
|
||||
version "7.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.12.1.tgz#2be6fa5c2529a2a75abac4d00aca786362302a1a"
|
||||
integrity sha512-pgyL65CrGFLe8sKcEG8KXAuVTE8zkAsyTlv/AuME06cSdxzO/memPK/r3BI6EM7WupIdga+V5tQUldeT1kgHNA==
|
||||
dependencies:
|
||||
"@sentry/core" "6.19.7"
|
||||
"@sentry/types" "6.19.7"
|
||||
"@sentry/utils" "6.19.7"
|
||||
"@sentry/core" "7.12.1"
|
||||
"@sentry/types" "7.12.1"
|
||||
"@sentry/utils" "7.12.1"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/core@6.19.7":
|
||||
version "6.19.7"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-6.19.7.tgz#156aaa56dd7fad8c89c145be6ad7a4f7209f9785"
|
||||
integrity sha512-tOfZ/umqB2AcHPGbIrsFLcvApdTm9ggpi/kQZFkej7kMphjT+SGBiQfYtjyg9jcRW+ilAR4JXC9BGKsdEQ+8Vw==
|
||||
"@sentry/core@7.12.1":
|
||||
version "7.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.12.1.tgz#a22f1c530ed528a699ed204c36eb5fc8d308103d"
|
||||
integrity sha512-DFHbzHFjukhlkRZ5xzfebx0IBzblW43kmfnalBBq7xEMscUvnhsYnlvL9Y20tuPZ/PrTcq4JAHbFluAvw6M0QQ==
|
||||
dependencies:
|
||||
"@sentry/hub" "6.19.7"
|
||||
"@sentry/minimal" "6.19.7"
|
||||
"@sentry/types" "6.19.7"
|
||||
"@sentry/utils" "6.19.7"
|
||||
"@sentry/hub" "7.12.1"
|
||||
"@sentry/types" "7.12.1"
|
||||
"@sentry/utils" "7.12.1"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/hub@6.19.7":
|
||||
version "6.19.7"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.19.7.tgz#58ad7776bbd31e9596a8ec46365b45cd8b9cfd11"
|
||||
integrity sha512-y3OtbYFAqKHCWezF0EGGr5lcyI2KbaXW2Ik7Xp8Mu9TxbSTuwTe4rTntwg8ngPjUQU3SUHzgjqVB8qjiGqFXCA==
|
||||
"@sentry/hub@7.12.1":
|
||||
version "7.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-7.12.1.tgz#dffad40cd2b8f44df2d5f20a89df87879cbbf1c3"
|
||||
integrity sha512-KLVnVqXf+CRmXNy9/T8K2/js7QvOQ94xtgP5KnWJbu2rl+JhxnIGiBRF51lPXFIatt7zWwB9qNdMS8lVsvLMGQ==
|
||||
dependencies:
|
||||
"@sentry/types" "6.19.7"
|
||||
"@sentry/utils" "6.19.7"
|
||||
"@sentry/types" "7.12.1"
|
||||
"@sentry/utils" "7.12.1"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/minimal@6.19.7":
|
||||
version "6.19.7"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.19.7.tgz#b3ee46d6abef9ef3dd4837ebcb6bdfd01b9aa7b4"
|
||||
integrity sha512-wcYmSJOdvk6VAPx8IcmZgN08XTXRwRtB1aOLZm+MVHjIZIhHoBGZJYTVQS/BWjldsamj2cX3YGbGXNunaCfYJQ==
|
||||
dependencies:
|
||||
"@sentry/hub" "6.19.7"
|
||||
"@sentry/types" "6.19.7"
|
||||
tslib "^1.9.3"
|
||||
"@sentry/types@7.12.1":
|
||||
version "7.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.12.1.tgz#eff76d938f9effc62a2ec76cd5c3f04de37f5c15"
|
||||
integrity sha512-VGZs39SZgMcCGv7H0VyFy1LEFGsnFZH590JUopmz6nG63EpeYQ2xzhIoPNAiLKbyUvBEwukn+faCg3u3MGqhgQ==
|
||||
|
||||
"@sentry/types@6.19.7":
|
||||
version "6.19.7"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.19.7.tgz#c6b337912e588083fc2896eb012526cf7cfec7c7"
|
||||
integrity sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg==
|
||||
|
||||
"@sentry/utils@6.19.7":
|
||||
version "6.19.7"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.19.7.tgz#6edd739f8185fd71afe49cbe351c1bbf5e7b7c79"
|
||||
integrity sha512-z95ECmE3i9pbWoXQrD/7PgkBAzJYR+iXtPuTkpBjDKs86O3mT+PXOT3BAn79w2wkn7/i3vOGD2xVr1uiMl26dA==
|
||||
"@sentry/utils@7.12.1":
|
||||
version "7.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.12.1.tgz#fcf80fdc332d0bd288e21b13efc7a2f0d604f75a"
|
||||
integrity sha512-Dh8B13pC0u8uLM/zf+oZngyg808c6BDEO94F7H+h3IciCVVd92A0cOQwLGAEdf8srnJgpZJNAlSC8lFDhbFHzQ==
|
||||
dependencies:
|
||||
"@sentry/types" "6.19.7"
|
||||
"@sentry/types" "7.12.1"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@trysound/sax@0.2.0":
|
||||
@ -1085,9 +1076,9 @@
|
||||
"@types/estree" "*"
|
||||
|
||||
"@types/eslint@*":
|
||||
version "8.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.5.tgz#acdfb7dd36b91cc5d812d7c093811a8f3d9b31e4"
|
||||
integrity sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==
|
||||
version "8.4.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.6.tgz#7976f054c1bccfcf514bff0564c0c41df5c08207"
|
||||
integrity sha512-/fqTbjxyFUaYNO7VcW5g+4npmqVACz1bB7RTHYuLj+PRjw9hrCwrUXVQFpChUS0JsyEFvMZ7U/PfmvWgxJhI9g==
|
||||
dependencies:
|
||||
"@types/estree" "*"
|
||||
"@types/json-schema" "*"
|
||||
@ -1144,9 +1135,9 @@
|
||||
integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==
|
||||
|
||||
"@types/node@*":
|
||||
version "18.7.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.6.tgz#31743bc5772b6ac223845e18c3fc26f042713c83"
|
||||
integrity sha512-EdxgKRXgYsNITy5mjjXjVE/CS8YENSdhiagGrLqjG0pvA2owgJ6i4l7wy/PFZGC0B1/H20lWKN7ONVDNYDZm7A==
|
||||
version "18.7.16"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.16.tgz#0eb3cce1e37c79619943d2fd903919fc30850601"
|
||||
integrity sha512-EQHhixfu+mkqHMZl1R2Ovuvn47PUw18azMJOTwSZr9/fhzHNGXAJ0ma0dayRVchprpCj0Kc1K1xKoWaATWF1qg==
|
||||
|
||||
"@types/qs@*":
|
||||
version "6.9.7"
|
||||
@ -1524,9 +1515,9 @@ body-parser@1.20.0:
|
||||
unpipe "1.0.0"
|
||||
|
||||
bonjour-service@^1.0.11:
|
||||
version "1.0.13"
|
||||
resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.0.13.tgz#4ac003dc1626023252d58adf2946f57e5da450c1"
|
||||
integrity sha512-LWKRU/7EqDUC9CTAQtuZl5HzBALoCYwtLhffW3et7vZMwv3bWLpJf8bRYlMD5OCcDpTfnPgNCV4yo9ZIaJGMiA==
|
||||
version "1.0.14"
|
||||
resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.0.14.tgz#c346f5bc84e87802d08f8d5a60b93f758e514ee7"
|
||||
integrity sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ==
|
||||
dependencies:
|
||||
array-flatten "^2.1.2"
|
||||
dns-equal "^1.0.0"
|
||||
@ -1538,10 +1529,10 @@ boolbase@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
||||
integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==
|
||||
|
||||
bootstrap@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.2.0.tgz#838727fb60f1630db370fe57c63cbcf2962bb3d3"
|
||||
integrity sha512-qlnS9GL6YZE6Wnef46GxGv1UpGGzAwO0aPL1yOjzDIJpeApeMvqV24iL+pjr2kU4dduoBA9fINKWKgMToobx9A==
|
||||
bootstrap@^5.2.1:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.2.1.tgz#45f97ff05cbe828bad807b014d8425f3aeb8ec3a"
|
||||
integrity sha512-UQi3v2NpVPEi1n35dmRRzBJFlgvWHYwyem6yHhuT6afYF+sziEt46McRbT//kVXZ7b1YUYEVGdXEH74Nx3xzGA==
|
||||
|
||||
bootswatch@^5.2.0:
|
||||
version "5.2.0"
|
||||
@ -1607,9 +1598,9 @@ caniuse-api@^3.0.0:
|
||||
lodash.uniq "^4.5.0"
|
||||
|
||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001370:
|
||||
version "1.0.30001378"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001378.tgz#3d2159bf5a8f9ca093275b0d3ecc717b00f27b67"
|
||||
integrity sha512-JVQnfoO7FK7WvU4ZkBRbPjaot4+YqxogSDosHv0Hv5mWpUESmN+UubMU6L/hGz8QlQ2aY5U0vR6MOs6j/CXpNA==
|
||||
version "1.0.30001393"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001393.tgz#1aa161e24fe6af2e2ccda000fc2b94be0b0db356"
|
||||
integrity sha512-N/od11RX+Gsk+1qY/jbPa0R6zJupEa0lxeBG598EbrtblxVCTJsQwbRBm6+V+rxpc5lHKdsXb9RY83cZIPLseA==
|
||||
|
||||
chalk@^2.0.0:
|
||||
version "2.4.2"
|
||||
@ -1784,12 +1775,11 @@ cookie@0.5.0:
|
||||
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
|
||||
|
||||
core-js-compat@^3.21.0, core-js-compat@^3.22.1:
|
||||
version "3.24.1"
|
||||
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.24.1.tgz#d1af84a17e18dfdd401ee39da9996f9a7ba887de"
|
||||
integrity sha512-XhdNAGeRnTpp8xbD+sR/HFDK9CbeeeqXT6TuofXh3urqEevzkWmLRgrVoykodsw8okqo2pu1BOmuCKrHx63zdw==
|
||||
version "3.25.1"
|
||||
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.25.1.tgz#6f13a90de52f89bbe6267e5620a412c7f7ff7e42"
|
||||
integrity sha512-pOHS7O0i8Qt4zlPW/eIFjwp+NrTPx+wTL0ctgI2fHn31sZOq89rDsmtc/A2vAX7r6shl+bmVI+678He46jgBlw==
|
||||
dependencies:
|
||||
browserslist "^4.21.3"
|
||||
semver "7.0.0"
|
||||
|
||||
core-util-is@~1.0.0:
|
||||
version "1.0.3"
|
||||
@ -1806,9 +1796,9 @@ cross-spawn@^7.0.3:
|
||||
which "^2.0.1"
|
||||
|
||||
css-declaration-sorter@^6.3.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.3.0.tgz#72ebd995c8f4532ff0036631f7365cce9759df14"
|
||||
integrity sha512-OGT677UGHJTAVMRhPO+HJ4oKln3wkBTwtDFH0ojbqm+MJm6xuDMHp2nkhh/ThaBqq20IbraBQSWKfSLNHQO9Og==
|
||||
version "6.3.1"
|
||||
resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz#be5e1d71b7a992433fb1c542c7a1b835e45682ec"
|
||||
integrity sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==
|
||||
|
||||
css-loader@^6.7.1:
|
||||
version "6.7.1"
|
||||
@ -2302,9 +2292,9 @@ ee-first@1.1.1:
|
||||
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
|
||||
|
||||
electron-to-chromium@^1.4.202:
|
||||
version "1.4.224"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.224.tgz#ecf2eed395cfedcbbe634658ccc4b457f7b254c3"
|
||||
integrity sha512-dOujC5Yzj0nOVE23iD5HKqrRSDj2SD7RazpZS/b/WX85MtO6/LzKDF4TlYZTBteB+7fvSg5JpWh0sN7fImNF8w==
|
||||
version "1.4.246"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.246.tgz#802132d1bbd3ff32ce82fcd6a6ed6ab59b4366dc"
|
||||
integrity sha512-/wFCHUE+Hocqr/LlVGsuKLIw4P2lBWwFIDcNMDpJGzyIysQV4aycpoOitAs32FT94EHKnNqDR/CVZJFbXEufJA==
|
||||
|
||||
emojis-list@^3.0.0:
|
||||
version "3.0.0"
|
||||
@ -2708,10 +2698,10 @@ human-signals@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
|
||||
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
|
||||
|
||||
i18n-js@^4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/i18n-js/-/i18n-js-4.0.2.tgz#31a62520204e292675bd72e82abdfeeb9aed5f20"
|
||||
integrity sha512-81geA9vPvWe4NI2uL8Ve+/2jM03Sj/IvGgFHPxij7DbSXbG3CfChRK1vBj2h2IRl6eVRZf3Eq4ug3PgeeWUUPA==
|
||||
i18n-js@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/i18n-js/-/i18n-js-4.1.1.tgz#05ebd03c4d92f6dc26a00d7c5cfb90f9c326b67c"
|
||||
integrity sha512-Uph8ghmfShexVhDcNtg5s40zprJZPrhW5iOEKsUwPiiBpIGN/0EJ9W7DTqhLFlWfAlpkFiaLxVxHsk8f+3KjIQ==
|
||||
dependencies:
|
||||
bignumber.js "*"
|
||||
lodash "*"
|
||||
@ -2882,10 +2872,10 @@ jquery-ujs@^1.2.3:
|
||||
resolved "https://registry.yarnpkg.com/jquery-ujs/-/jquery-ujs-1.2.3.tgz#dcac6026ab7268e5ee41faf9d31c997cd4ddd603"
|
||||
integrity sha512-59wvfx5vcCTHMeQT1/OwFiAj+UffLIwjRIoXdpO7Z7BCFGepzq9T9oLVeoItjTqjoXfUrHJvV7QU6pUR+UzOoA==
|
||||
|
||||
"jquery@>=1.8.0 <4.0.0", jquery@>=1.9.1, jquery@^3.6.0:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.0.tgz#c72a09f15c1bdce142f49dbf1170bdf8adac2470"
|
||||
integrity sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==
|
||||
"jquery@>=1.8.0 <4.0.0", jquery@>=1.9.1, jquery@^3.6.1:
|
||||
version "3.6.1"
|
||||
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.1.tgz#fab0408f8b45fc19f956205773b62b292c147a16"
|
||||
integrity sha512-opJeO4nCucVnsjiXOE+/PcCgYw9Gwpvs/a6B1LL/lQhwWwpbVEVYDZ1FokFr8PRc7ghYlrFPuyHuiiDNTQxmcw==
|
||||
|
||||
js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
@ -3830,10 +3820,10 @@ sass-loader@^13.0.2:
|
||||
klona "^2.0.4"
|
||||
neo-async "^2.6.2"
|
||||
|
||||
sass@^1.54.4:
|
||||
version "1.54.4"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.54.4.tgz#803ff2fef5525f1dd01670c3915b4b68b6cba72d"
|
||||
integrity sha512-3tmF16yvnBwtlPrNBHw/H907j8MlOX8aTBnlNX1yrKx24RKcJGPyLhFUwkoKBKesR3unP93/2z14Ll8NicwQUA==
|
||||
sass@^1.54.9:
|
||||
version "1.54.9"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.54.9.tgz#b05f14ed572869218d1a76961de60cd647221762"
|
||||
integrity sha512-xb1hjASzEH+0L0WI9oFjqhRi51t/gagWnxLiwUNMltA0Ab6jIDkAacgKiGYKM9Jhy109osM7woEEai6SXeJo5Q==
|
||||
dependencies:
|
||||
chokidar ">=3.0.0 <4.0.0"
|
||||
immutable "^4.0.0"
|
||||
@ -3873,17 +3863,12 @@ select-hose@^2.0.0:
|
||||
integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==
|
||||
|
||||
selfsigned@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.0.1.tgz#8b2df7fa56bf014d19b6007655fff209c0ef0a56"
|
||||
integrity sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.1.1.tgz#18a7613d714c0cd3385c48af0075abf3f266af61"
|
||||
integrity sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==
|
||||
dependencies:
|
||||
node-forge "^1"
|
||||
|
||||
semver@7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
|
||||
integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
|
||||
|
||||
semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||
@ -3955,10 +3940,10 @@ setprototypeof@1.2.0:
|
||||
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
|
||||
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
|
||||
|
||||
shakapacker@6.5.1:
|
||||
version "6.5.1"
|
||||
resolved "https://registry.yarnpkg.com/shakapacker/-/shakapacker-6.5.1.tgz#3c1554771c0498ea757cb86dfc06811875f4c2bd"
|
||||
integrity sha512-yOVvek0fxAU3FUjWcT7GxpJ/zAbhKbGEw78HHYfMAHE95cDhxTAQjBwvChP/PlceiteXUryRI0Mrdy7jqXvmnw==
|
||||
shakapacker@6.5.2:
|
||||
version "6.5.2"
|
||||
resolved "https://registry.yarnpkg.com/shakapacker/-/shakapacker-6.5.2.tgz#dda95543107a71c7ada3f6ee102a1a31563c6738"
|
||||
integrity sha512-32hpr/AuyQJEk/4J8quL/xLPl+NPR0mBvJ3D9AtwHIkbSTUA0++LZrvVO+aQ4S1Uy3Iz2KSI/JVRGGD/C4SFFg==
|
||||
dependencies:
|
||||
glob "^7.2.0"
|
||||
js-yaml "^4.1.0"
|
||||
@ -4148,10 +4133,10 @@ tapable@^2.0, tapable@^2.1.1, tapable@^2.2.0:
|
||||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0"
|
||||
integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==
|
||||
|
||||
terser-webpack-plugin@^5.1.3, terser-webpack-plugin@^5.3.5:
|
||||
version "5.3.5"
|
||||
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.5.tgz#f7d82286031f915a4f8fb81af4bd35d2e3c011bc"
|
||||
integrity sha512-AOEDLDxD2zylUGf/wxHxklEkOe2/r+seuyOWujejFrIxHf11brA1/dWQNIgXa1c6/Wkxgu7zvv0JhOWfc2ELEA==
|
||||
terser-webpack-plugin@^5.1.3, terser-webpack-plugin@^5.3.6:
|
||||
version "5.3.6"
|
||||
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz#5590aec31aa3c6f771ce1b1acca60639eab3195c"
|
||||
integrity sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==
|
||||
dependencies:
|
||||
"@jridgewell/trace-mapping" "^0.3.14"
|
||||
jest-worker "^27.4.5"
|
||||
@ -4160,9 +4145,9 @@ terser-webpack-plugin@^5.1.3, terser-webpack-plugin@^5.3.5:
|
||||
terser "^5.14.1"
|
||||
|
||||
terser@^5.14.1:
|
||||
version "5.14.2"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.2.tgz#9ac9f22b06994d736174f4091aa368db896f1c10"
|
||||
integrity sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==
|
||||
version "5.15.0"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.15.0.tgz#e16967894eeba6e1091509ec83f0c60e179f2425"
|
||||
integrity sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==
|
||||
dependencies:
|
||||
"@jridgewell/source-map" "^0.3.2"
|
||||
acorn "^8.5.0"
|
||||
@ -4209,6 +4194,11 @@ type-is@~1.6.18:
|
||||
media-typer "0.3.0"
|
||||
mime-types "~2.1.24"
|
||||
|
||||
typed-assert@^1.0.8:
|
||||
version "1.0.9"
|
||||
resolved "https://registry.yarnpkg.com/typed-assert/-/typed-assert-1.0.9.tgz#8af9d4f93432c4970ec717e3006f33f135b06213"
|
||||
integrity sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==
|
||||
|
||||
underscore@^1.13.4:
|
||||
version "1.13.4"
|
||||
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.4.tgz#7886b46bbdf07f768e0052f1828e1dcab40c0dee"
|
||||
@ -4243,9 +4233,9 @@ unpipe@1.0.0, unpipe@~1.0.0:
|
||||
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
|
||||
|
||||
update-browserslist-db@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz#be06a5eedd62f107b7c19eb5bcefb194411abf38"
|
||||
integrity sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.7.tgz#16279639cff1d0f800b14792de43d97df2d11b7d"
|
||||
integrity sha512-iN/XYesmZ2RmmWAiI4Z5rq0YqSiv0brj9Ce9CfhNE4xIW2h+MFxcgkxIzZ+ShkFPUkjU3gQ+3oypadD3RAMtrg==
|
||||
dependencies:
|
||||
escalade "^3.1.1"
|
||||
picocolors "^1.0.0"
|
||||
@ -4349,10 +4339,10 @@ webpack-dev-middleware@^5.3.1:
|
||||
range-parser "^1.2.1"
|
||||
schema-utils "^4.0.0"
|
||||
|
||||
webpack-dev-server@^4.10.0:
|
||||
version "4.10.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.10.0.tgz#de270d0009eba050546912be90116e7fd740a9ca"
|
||||
integrity sha512-7dezwAs+k6yXVFZ+MaL8VnE+APobiO3zvpp3rBHe/HmWQ+avwh0Q3d0xxacOiBybZZ3syTZw9HXzpa3YNbAZDQ==
|
||||
webpack-dev-server@^4.11.0:
|
||||
version "4.11.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.11.0.tgz#290ee594765cd8260adfe83b2d18115ea04484e7"
|
||||
integrity sha512-L5S4Q2zT57SK7tazgzjMiSMBdsw+rGYIX27MgPgx7LDhWO0lViPrHKoLS7jo5In06PWYAhlYu3PbyoC6yAThbw==
|
||||
dependencies:
|
||||
"@types/bonjour" "^3.5.9"
|
||||
"@types/connect-history-api-fallback" "^1.3.5"
|
||||
@ -4397,6 +4387,13 @@ webpack-sources@^3.2.3:
|
||||
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
|
||||
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
|
||||
|
||||
webpack-subresource-integrity@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz#8b7606b033c6ccac14e684267cb7fb1f5c2a132a"
|
||||
integrity sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==
|
||||
dependencies:
|
||||
typed-assert "^1.0.8"
|
||||
|
||||
webpack@^5.74.0:
|
||||
version "5.74.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.74.0.tgz#02a5dac19a17e0bb47093f2be67c695102a55980"
|
||||
|
Reference in New Issue
Block a user