always `loadScript` with a script tag (#6411)

to avoid Content Security Policy unsafe-line violations
This commit is contained in:
Kyle Zhao 2018-10-01 10:06:01 +08:00 committed by GitHub
parent 38b48997fc
commit 373d6e3fe6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 34 additions and 24 deletions

View File

@ -1,8 +1,6 @@
import loadScript from "discourse/lib/load-script";
import { observes } from "ember-addons/ember-computed-decorators";
const LOAD_ASYNC = !Ember.testing;
export default Ember.Component.extend({
mode: "css",
classNames: ["ace-wrapper"],
@ -26,7 +24,7 @@ export default Ember.Component.extend({
@observes("mode")
modeChanged() {
if (LOAD_ASYNC && this._editor && !this._skipContentChangeEvent) {
if (this._editor && !this._skipContentChangeEvent) {
this._editor.getSession().setMode("ace/mode/" + this.get("mode"));
}
},
@ -71,21 +69,17 @@ export default Ember.Component.extend({
didInsertElement() {
this._super();
loadScript("/javascripts/ace/ace.js", { scriptTag: true }).then(() => {
loadScript("/javascripts/ace/ace.js").then(() => {
window.ace.require(["ace/ace"], loadedAce => {
if (!this.element || this.isDestroying || this.isDestroyed) {
return;
}
const editor = loadedAce.edit(this.$(".ace")[0]);
if (LOAD_ASYNC) {
editor.setTheme("ace/theme/chrome");
}
editor.setTheme("ace/theme/chrome");
editor.setShowPrintMargin(false);
editor.setOptions({ fontSize: "14px" });
if (LOAD_ASYNC) {
editor.getSession().setMode("ace/mode/" + this.get("mode"));
}
editor.getSession().setMode("ace/mode/" + this.get("mode"));
editor.on("change", () => {
this._skipContentChangeEvent = true;
this.set("content", editor.getSession().getValue());

View File

@ -44,7 +44,7 @@ export default function loadScript(url, opts) {
$("script").each((i, tag) => {
const src = tag.getAttribute("src");
if (src && (opts.scriptTag || src !== url)) {
if (src && src !== url) {
_loaded[tag.getAttribute("src")] = true;
}
});
@ -86,22 +86,15 @@ export default function loadScript(url, opts) {
cdnUrl = Discourse.CDN.replace(/\/$/, "") + url;
}
// Some javascript depends on the path of where it is loaded (ace editor)
// to dynamically load more JS. In that case, add the `scriptTag: true`
// option.
if (opts.scriptTag) {
if (Ember.testing) {
throw new Error(
`In test mode scripts cannot be loaded async ${cdnUrl}`
);
}
loadWithTag(cdnUrl, cb);
} else {
if (opts.css) {
ajax({
url: cdnUrl,
dataType: opts.css ? "text" : "script",
dataType: "text",
cache: true
}).then(cb);
} else {
// Always load JavaScript with script tag to avoid Content Security Policy inline violations
loadWithTag(cdnUrl, cb);
}
});
}

View File

@ -0,0 +1,24 @@
import loadScript from "discourse/lib/load-script";
QUnit.module("lib:load-script");
QUnit.test(
"load with a script tag, and callbacks are only executed after script is loaded",
async assert => {
const src = "/javascripts/ace/ace.js";
await loadScript(src).then(() => {
assert.ok(
typeof ace !== "undefined",
"callbacks should only be executed after the script has fully loaded"
);
// cannot use the `find` test helper here because the script tag is injected outside of the test sandbox frame
const scriptTags = Array.from(document.getElementsByTagName("script"));
assert.ok(
scriptTags.some(scriptTag => scriptTag.src.includes(src)),
"the script should be loaded with a script tag"
);
});
}
);

View File

@ -52,7 +52,6 @@ window.MessageBus.stop();
// Trick JSHint into allow document.write
var d = document;
d.write('<script src="/javascripts/ace/ace.js"></script>');
d.write(
'<div id="ember-testing-container"><div id="ember-testing"></div></div>'
);