SECURITY: Remove ember-cli specific response from application routes (#15155)

Under some conditions, these varied responses could lead to cache poisoning, hence the 'security' label.

Previously the Rails application would serve JSON data in place of HTML whenever Ember CLI requested an `application.html.erb`-rendered page. This commit removes that logic, and instead parses the HTML out of the standard response. This means that Rails doesn't need to customize its response for Ember CLI.
This commit is contained in:
David Taylor 2021-12-01 16:10:40 +00:00 committed by GitHub
parent f37375f582
commit 1fa7a87f86
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 265 additions and 77 deletions

View File

@ -25,6 +25,9 @@ module.exports = function (defaults) {
// This forces the use of `fast-sourcemap-concat` which works in production. // This forces the use of `fast-sourcemap-concat` which works in production.
enabled: true, enabled: true,
}, },
autoImport: {
forbidEval: true,
},
}); });
// Ember CLI does this by default for the app tree, but for our extra bundles we // Ember CLI does this by default for the app tree, but for our extra bundles we

View File

@ -6,6 +6,7 @@ const { encode } = require("html-entities");
const cleanBaseURL = require("clean-base-url"); const cleanBaseURL = require("clean-base-url");
const path = require("path"); const path = require("path");
const { promises: fs } = require("fs"); const { promises: fs } = require("fs");
const { JSDOM } = require("jsdom");
// via https://stackoverflow.com/a/6248722/165668 // via https://stackoverflow.com/a/6248722/165668
function generateUID() { function generateUID() {
@ -168,12 +169,14 @@ function replaceIn(bootstrap, template, id, headers, baseURL) {
return template.replace(`<bootstrap-content key="${id}">`, contents); return template.replace(`<bootstrap-content key="${id}">`, contents);
} }
async function applyBootstrap(bootstrap, template, response, baseURL) { function extractPreloadJson(html) {
// If our initial page added some preload data let's not lose that. const dom = new JSDOM(html);
let json = await response.json(); return dom.window.document.querySelector("#data-preloaded")?.dataset
if (json && json.preloaded) { ?.preloaded;
bootstrap.preloaded = Object.assign(json.preloaded, bootstrap.preloaded); }
}
async function applyBootstrap(bootstrap, template, response, baseURL, preload) {
bootstrap.preloaded = Object.assign(JSON.parse(preload), bootstrap.preloaded);
Object.keys(BUILDERS).forEach((id) => { Object.keys(BUILDERS).forEach((id) => {
template = replaceIn(bootstrap, template, id, response.headers, baseURL); template = replaceIn(bootstrap, template, id, response.headers, baseURL);
@ -181,23 +184,20 @@ async function applyBootstrap(bootstrap, template, response, baseURL) {
return template; return template;
} }
async function buildFromBootstrap(proxy, baseURL, req, response) { async function buildFromBootstrap(proxy, baseURL, req, response, preload) {
try { try {
const template = await fs.readFile( const template = await fs.readFile(
path.join(process.cwd(), "dist", "index.html"), path.join(process.cwd(), "dist", "index.html"),
"utf8" "utf8"
); );
let url = `${proxy}${baseURL}bootstrap.json`; let url = new URL(`${proxy}${baseURL}bootstrap.json`);
const queryLoc = req.url.indexOf("?"); url.searchParams.append("for_url", req.url);
if (queryLoc !== -1) {
url += req.url.substr(queryLoc);
}
const res = await fetch(url, { headers: req.headers }); const res = await fetch(url, { headers: req.headers });
const json = await res.json(); const json = await res.json();
return applyBootstrap(json.bootstrap, template, response, baseURL); return applyBootstrap(json.bootstrap, template, response, baseURL, preload);
} catch (error) { } catch (error) {
throw new Error( throw new Error(
`Could not get ${proxy}${baseURL}bootstrap.json\n\n${error}` `Could not get ${proxy}${baseURL}bootstrap.json\n\n${error}`
@ -229,7 +229,6 @@ async function handleRequest(proxy, baseURL, req, res) {
if (req.method === "GET") { if (req.method === "GET") {
req.headers["X-Discourse-Ember-CLI"] = "true"; req.headers["X-Discourse-Ember-CLI"] = "true";
req.headers["X-Discourse-Asset-Path"] = req.path;
} }
const response = await fetch(url, { const response = await fetch(url, {
@ -251,20 +250,37 @@ async function handleRequest(proxy, baseURL, req, res) {
const csp = response.headers.get("content-security-policy"); const csp = response.headers.get("content-security-policy");
if (csp) { if (csp) {
const newCSP = csp.replace( const emberCliAdditions = [
new RegExp(proxy, "g"), `http://${originalHost}/assets/`,
`http://${originalHost}` `http://${originalHost}/ember-cli-live-reload.js`,
); `http://${originalHost}/_lr/`,
];
const newCSP = csp
.replace(new RegExp(proxy, "g"), `http://${originalHost}`)
.replace(
new RegExp("script-src ", "g"),
`script-src ${emberCliAdditions.join(" ")} `
);
res.set("content-security-policy", newCSP); res.set("content-security-policy", newCSP);
} }
if (response.headers.get("x-discourse-bootstrap-required") === "true") { const isHTML = response.headers["content-type"]?.startsWith("text/html");
const html = await buildFromBootstrap(proxy, baseURL, req, response); const responseText = await response.text();
const preload = isHTML ? extractPreloadJson(responseText) : null;
if (preload) {
const html = await buildFromBootstrap(
proxy,
baseURL,
req,
response,
preload
);
res.set("content-type", "text/html"); res.set("content-type", "text/html");
res.send(html); res.send(html);
} else { } else {
res.status(response.status); res.status(response.status);
res.send(await response.text()); res.send(responseText);
} }
} }

View File

@ -54,6 +54,7 @@
"eslint": "^7.27.0", "eslint": "^7.27.0",
"html-entities": "^2.1.0", "html-entities": "^2.1.0",
"js-yaml": "^4.0.0", "js-yaml": "^4.0.0",
"jsdom": "^18.1.1",
"loader.js": "^4.7.0", "loader.js": "^4.7.0",
"message-bus-client": "^3.3.0", "message-bus-client": "^3.3.0",
"messageformat": "0.1.5", "messageformat": "0.1.5",

View File

@ -1317,6 +1317,11 @@
resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5"
integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==
"@tootallnate/once@2":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==
"@transloadit/prettier-bytes@0.0.7": "@transloadit/prettier-bytes@0.0.7":
version "0.0.7" version "0.0.7"
resolved "https://registry.yarnpkg.com/@transloadit/prettier-bytes/-/prettier-bytes-0.0.7.tgz#cdb5399f445fdd606ed833872fa0cabdbc51686b" resolved "https://registry.yarnpkg.com/@transloadit/prettier-bytes/-/prettier-bytes-0.0.7.tgz#cdb5399f445fdd606ed833872fa0cabdbc51686b"
@ -1715,11 +1720,23 @@ acorn@^8.1.0:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.1.1.tgz#fb0026885b9ac9f48bac1e185e4af472971149ff" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.1.1.tgz#fb0026885b9ac9f48bac1e185e4af472971149ff"
integrity sha512-xYiIVjNuqtKXMxlRMDc6mZUhXehod4a3gbZ1qRlM7icK4EbxUFNLhWoPblCvFtB2Y9CIqHP3CF/rdxLItaQv8g== integrity sha512-xYiIVjNuqtKXMxlRMDc6mZUhXehod4a3gbZ1qRlM7icK4EbxUFNLhWoPblCvFtB2Y9CIqHP3CF/rdxLItaQv8g==
acorn@^8.5.0:
version "8.6.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.6.0.tgz#e3692ba0eb1a0c83eaa4f37f5fa7368dd7142895"
integrity sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==
after@0.8.2: after@0.8.2:
version "0.8.2" version "0.8.2"
resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f"
integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=
agent-base@6:
version "6.0.2"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
dependencies:
debug "4"
ajv-errors@^1.0.0: ajv-errors@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d"
@ -4080,7 +4097,7 @@ colors@^1.1.2:
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
combined-stream@^1.0.6, combined-stream@~1.0.6: combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
version "1.0.8" version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
@ -4405,6 +4422,11 @@ cssom@^0.4.4:
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10"
integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==
cssom@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36"
integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==
cssom@~0.3.6: cssom@~0.3.6:
version "0.3.8" version "0.3.8"
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a"
@ -4443,6 +4465,15 @@ data-urls@^2.0.0:
whatwg-mimetype "^2.3.0" whatwg-mimetype "^2.3.0"
whatwg-url "^8.0.0" whatwg-url "^8.0.0"
data-urls@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.1.tgz#597fc2ae30f8bc4dbcf731fcd1b1954353afc6f8"
integrity sha512-Ds554NeT5Gennfoo9KN50Vh6tpgtvYEwraYjejXnyTpu1C7oXKxdFk75REooENHE8ndTVOJuv+BEs4/J/xcozw==
dependencies:
abab "^2.0.3"
whatwg-mimetype "^3.0.0"
whatwg-url "^10.0.0"
debug@2.6.9, debug@^2.1.0, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: debug@2.6.9, debug@^2.1.0, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9:
version "2.6.9" version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@ -4450,6 +4481,13 @@ debug@2.6.9, debug@^2.1.0, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.3.
dependencies: dependencies:
ms "2.0.0" ms "2.0.0"
debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0:
version "4.3.2"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
dependencies:
ms "2.1.2"
debug@^3.0.1, debug@^3.1.0, debug@^3.1.1: debug@^3.0.1, debug@^3.1.0, debug@^3.1.1:
version "3.2.7" version "3.2.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
@ -4457,13 +4495,6 @@ debug@^3.0.1, debug@^3.1.0, debug@^3.1.1:
dependencies: dependencies:
ms "^2.1.1" ms "^2.1.1"
debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0:
version "4.3.2"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
dependencies:
ms "2.1.2"
debug@~3.1.0: debug@~3.1.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
@ -4483,6 +4514,11 @@ decimal.js@^10.2.1:
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.1.tgz#238ae7b0f0c793d3e3cea410108b35a2c01426a3" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.1.tgz#238ae7b0f0c793d3e3cea410108b35a2c01426a3"
integrity sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw== integrity sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw==
decimal.js@^10.3.1:
version "10.3.1"
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783"
integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==
decode-uri-component@^0.2.0: decode-uri-component@^0.2.0:
version "0.2.0" version "0.2.0"
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
@ -4653,6 +4689,13 @@ domexception@^2.0.1:
dependencies: dependencies:
webidl-conversions "^5.0.0" webidl-conversions "^5.0.0"
domexception@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673"
integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==
dependencies:
webidl-conversions "^7.0.0"
dot-case@^3.0.4: dot-case@^3.0.4:
version "3.0.4" version "3.0.4"
resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
@ -6391,6 +6434,15 @@ forever-agent@~0.6.1:
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
form-data@~2.3.2: form-data@~2.3.2:
version "2.3.3" version "2.3.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
@ -7122,6 +7174,13 @@ html-encoding-sniffer@^2.0.1:
dependencies: dependencies:
whatwg-encoding "^1.0.5" whatwg-encoding "^1.0.5"
html-encoding-sniffer@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9"
integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==
dependencies:
whatwg-encoding "^2.0.0"
html-entities@^2.1.0: html-entities@^2.1.0:
version "2.3.2" version "2.3.2"
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.2.tgz#760b404685cb1d794e4f4b744332e3b00dcfe488" resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.2.tgz#760b404685cb1d794e4f4b744332e3b00dcfe488"
@ -7169,6 +7228,15 @@ http-parser-js@>=0.5.1:
resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.3.tgz#01d2709c79d41698bb01d4decc5e9da4e4a033d9" resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.3.tgz#01d2709c79d41698bb01d4decc5e9da4e4a033d9"
integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg== integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==
http-proxy-agent@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43"
integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==
dependencies:
"@tootallnate/once" "2"
agent-base "6"
debug "4"
http-proxy@^1.13.1, http-proxy@^1.18.1: http-proxy@^1.13.1, http-proxy@^1.18.1:
version "1.18.1" version "1.18.1"
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
@ -7192,6 +7260,14 @@ https-browserify@^1.0.0:
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
https-proxy-agent@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2"
integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==
dependencies:
agent-base "6"
debug "4"
https@^1.0.0: https@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/https/-/https-1.0.0.tgz#3c37c7ae1a8eeb966904a2ad1e975a194b7ed3a4" resolved "https://registry.yarnpkg.com/https/-/https-1.0.0.tgz#3c37c7ae1a8eeb966904a2ad1e975a194b7ed3a4"
@ -7209,6 +7285,13 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24:
dependencies: dependencies:
safer-buffer ">= 2.1.2 < 3" safer-buffer ">= 2.1.2 < 3"
iconv-lite@0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
dependencies:
safer-buffer ">= 2.1.2 < 3.0.0"
ieee754@^1.1.4: ieee754@^1.1.4:
version "1.2.1" version "1.2.1"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
@ -7584,7 +7667,7 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4:
dependencies: dependencies:
isobject "^3.0.1" isobject "^3.0.1"
is-potential-custom-element-name@^1.0.0: is-potential-custom-element-name@^1.0.0, is-potential-custom-element-name@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5"
integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==
@ -7803,6 +7886,39 @@ jsdom@^16.4.0:
ws "^7.4.4" ws "^7.4.4"
xml-name-validator "^3.0.0" xml-name-validator "^3.0.0"
jsdom@^18.1.1:
version "18.1.1"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-18.1.1.tgz#15ec896f5ab7df9669a62375606f47c8c09551aa"
integrity sha512-NmJQbjQ/gpS/1at/ce3nCx89HbXL/f5OcenBe8wU1Eik0ROhyUc3LtmG3567dEHAGXkN8rmILW/qtCOPxPHQJw==
dependencies:
abab "^2.0.5"
acorn "^8.5.0"
acorn-globals "^6.0.0"
cssom "^0.5.0"
cssstyle "^2.3.0"
data-urls "^3.0.1"
decimal.js "^10.3.1"
domexception "^4.0.0"
escodegen "^2.0.0"
form-data "^4.0.0"
html-encoding-sniffer "^3.0.0"
http-proxy-agent "^5.0.0"
https-proxy-agent "^5.0.0"
is-potential-custom-element-name "^1.0.1"
nwsapi "^2.2.0"
parse5 "6.0.1"
saxes "^5.0.1"
symbol-tree "^3.2.4"
tough-cookie "^4.0.0"
w3c-hr-time "^1.0.2"
w3c-xmlserializer "^3.0.0"
webidl-conversions "^7.0.0"
whatwg-encoding "^2.0.0"
whatwg-mimetype "^3.0.0"
whatwg-url "^10.0.0"
ws "^8.2.3"
xml-name-validator "^4.0.0"
jsesc@^1.3.0: jsesc@^1.3.0:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
@ -10208,7 +10324,7 @@ safe-regex@^1.1.0:
dependencies: dependencies:
ret "~0.1.10" ret "~0.1.10"
"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2" version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
@ -11272,6 +11388,13 @@ tr46@^2.0.2:
dependencies: dependencies:
punycode "^2.1.1" punycode "^2.1.1"
tr46@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9"
integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==
dependencies:
punycode "^2.1.1"
tr46@~0.0.3: tr46@~0.0.3:
version "0.0.3" version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
@ -11688,6 +11811,13 @@ w3c-xmlserializer@^2.0.0:
dependencies: dependencies:
xml-name-validator "^3.0.0" xml-name-validator "^3.0.0"
w3c-xmlserializer@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz#06cdc3eefb7e4d0b20a560a5a3aeb0d2d9a65923"
integrity sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==
dependencies:
xml-name-validator "^4.0.0"
walk-sync@^0.2.5: walk-sync@^0.2.5:
version "0.2.7" version "0.2.7"
resolved "https://registry.yarnpkg.com/walk-sync/-/walk-sync-0.2.7.tgz#b49be4ee6867657aeb736978b56a29d10fa39969" resolved "https://registry.yarnpkg.com/walk-sync/-/walk-sync-0.2.7.tgz#b49be4ee6867657aeb736978b56a29d10fa39969"
@ -11785,6 +11915,11 @@ webidl-conversions@^6.1.0:
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514"
integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==
webidl-conversions@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a"
integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==
webpack-sources@^1.4.0, webpack-sources@^1.4.1: webpack-sources@^1.4.0, webpack-sources@^1.4.1:
version "1.4.3" version "1.4.3"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933"
@ -11843,11 +11978,31 @@ whatwg-encoding@^1.0.5:
dependencies: dependencies:
iconv-lite "0.4.24" iconv-lite "0.4.24"
whatwg-encoding@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53"
integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==
dependencies:
iconv-lite "0.6.3"
whatwg-mimetype@^2.3.0: whatwg-mimetype@^2.3.0:
version "2.3.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
whatwg-mimetype@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7"
integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==
whatwg-url@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-10.0.0.tgz#37264f720b575b4a311bd4094ed8c760caaa05da"
integrity sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==
dependencies:
tr46 "^3.0.0"
webidl-conversions "^7.0.0"
whatwg-url@^5.0.0: whatwg-url@^5.0.0:
version "5.0.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
@ -11987,6 +12142,11 @@ ws@^7.4.4, ws@~7.4.2:
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.4.tgz#383bc9742cb202292c9077ceab6f6047b17f2d59" resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.4.tgz#383bc9742cb202292c9077ceab6f6047b17f2d59"
integrity sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw== integrity sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw==
ws@^8.2.3:
version "8.3.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.3.0.tgz#7185e252c8973a60d57170175ff55fdbd116070d"
integrity sha512-Gs5EZtpqZzLvmIM59w4igITU57lrtYVFneaa434VROv4thzJyV6UjIL3D42lslWlI+D4KzLYnxSwtfuiO79sNw==
x-is-array@0.1.0: x-is-array@0.1.0:
version "0.1.0" version "0.1.0"
resolved "https://registry.yarnpkg.com/x-is-array/-/x-is-array-0.1.0.tgz#de520171d47b3f416f5587d629b89d26b12dc29d" resolved "https://registry.yarnpkg.com/x-is-array/-/x-is-array-0.1.0.tgz#de520171d47b3f416f5587d629b89d26b12dc29d"
@ -12007,6 +12167,11 @@ xml-name-validator@^3.0.0:
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
xml-name-validator@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835"
integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==
xmlchars@^2.2.0: xmlchars@^2.2.0:
version "2.2.0" version "2.2.0"
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"

View File

@ -119,23 +119,9 @@ class ApplicationController < ActionController::Base
class RenderEmpty < StandardError; end class RenderEmpty < StandardError; end
class PluginDisabled < StandardError; end class PluginDisabled < StandardError; end
class EmberCLIHijacked < StandardError; end
def catch_ember_cli_hijack
yield
rescue ActionView::Template::Error => ex
raise ex unless ex.cause.is_a?(EmberCLIHijacked)
send_ember_cli_bootstrap
end
rescue_from RenderEmpty do rescue_from RenderEmpty do
catch_ember_cli_hijack do with_resolved_locale { render 'default/empty' }
with_resolved_locale { render 'default/empty' }
end
end
rescue_from EmberCLIHijacked do
send_ember_cli_bootstrap
end end
rescue_from ArgumentError do |e| rescue_from ArgumentError do |e|
@ -324,21 +310,13 @@ class ApplicationController < ActionController::Base
rescue Discourse::InvalidAccess rescue Discourse::InvalidAccess
return render plain: message, status: status_code return render plain: message, status: status_code
end end
catch_ember_cli_hijack do with_resolved_locale do
with_resolved_locale do error_page_opts[:layout] = opts[:include_ember] ? 'application' : 'no_ember'
error_page_opts[:layout] = opts[:include_ember] ? 'application' : 'no_ember' render html: build_not_found_page(error_page_opts)
render html: build_not_found_page(error_page_opts)
end
end end
end end
end end
def send_ember_cli_bootstrap
response.headers['X-Discourse-Bootstrap-Required'] = true
response.headers['Content-Type'] = "application/json"
render json: { preloaded: @preloaded }
end
# If a controller requires a plugin, it will raise an exception if that plugin is # If a controller requires a plugin, it will raise an exception if that plugin is
# disabled. This allows plugins to be disabled programmatically. # disabled. This allows plugins to be disabled programmatically.
def self.requires_plugin(plugin_name) def self.requires_plugin(plugin_name)

View File

@ -27,12 +27,21 @@ class BootstrapController < ApplicationController
add_style(mobile_view? ? :mobile : :desktop) add_style(mobile_view? ? :mobile : :desktop)
end end
add_style(:admin) if staff? add_style(:admin) if staff?
assets_fake_request = ActionDispatch::Request.new(request.env.dup)
assets_for_url = params[:for_url]
if assets_for_url
path, query = assets_for_url.split("?", 2)
assets_fake_request.env["PATH_INFO"] = path
assets_fake_request.env["QUERY_STRING"] = query
end
Discourse.find_plugin_css_assets( Discourse.find_plugin_css_assets(
include_official: allow_plugins?, include_official: allow_plugins?,
include_unofficial: allow_third_party_plugins?, include_unofficial: allow_third_party_plugins?,
mobile_view: mobile_view?, mobile_view: mobile_view?,
desktop_view: !mobile_view?, desktop_view: !mobile_view?,
request: request request: assets_fake_request
).each do |file| ).each do |file|
add_style(file, plugin: true) add_style(file, plugin: true)
end end
@ -49,7 +58,7 @@ class BootstrapController < ApplicationController
plugin_js = Discourse.find_plugin_js_assets( plugin_js = Discourse.find_plugin_js_assets(
include_official: allow_plugins?, include_official: allow_plugins?,
include_unofficial: allow_third_party_plugins?, include_unofficial: allow_third_party_plugins?,
request: request request: assets_fake_request
).map { |f| script_asset_path(f) } ).map { |f| script_asset_path(f) }
bootstrap = { bootstrap = {

View File

@ -647,10 +647,4 @@ module ApplicationHelper
current_user ? nil : value current_user ? nil : value
end end
end end
def hijack_if_ember_cli!
if request.headers["HTTP_X_DISCOURSE_EMBER_CLI"] == "true"
raise ApplicationController::EmberCLIHijacked.new
end
end
end end

View File

@ -131,4 +131,3 @@
<%- end %> <%- end %>
</body> </body>
</html> </html>
<%- hijack_if_ember_cli! -%>

View File

@ -338,11 +338,6 @@ module Discourse
path = request.fullpath path = request.fullpath
result[:path] = path if path.present? result[:path] = path if path.present?
# When we bootstrap using the JSON method, we want to be able to filter assets on
# the path we're bootstrapping for.
asset_path = request.headers["HTTP_X_DISCOURSE_ASSET_PATH"]
result[:path] = asset_path if asset_path.present?
result result
end end

View File

@ -76,12 +76,6 @@ describe Discourse do
opts = Discourse.asset_filter_options(:js, req) opts = Discourse.asset_filter_options(:js, req)
expect(opts[:path]).to eq("/hello") expect(opts[:path]).to eq("/hello")
end end
it "overwrites the path if the asset path is present" do
req = stub(fullpath: "/bootstrap.json", headers: { "HTTP_X_DISCOURSE_ASSET_PATH" => "/hello" })
opts = Discourse.asset_filter_options(:js, req)
expect(opts[:path]).to eq("/hello")
end
end end
context 'plugins' do context 'plugins' do

View File

@ -93,4 +93,38 @@ describe BootstrapController do
expect(bootstrap['authentication_data']).to eq(cookie_data) expect(bootstrap['authentication_data']).to eq(cookie_data)
end end
end end
context 'with a plugin asset filter' do
let :plugin do
plugin = Plugin::Instance.new
plugin.path = "#{Rails.root}/spec/fixtures/plugins/my_plugin/plugin.rb"
plugin.register_asset_filter do |type, request|
next true if request.path == "/mypluginroute"
false
end
plugin
end
before do
Discourse.plugins << plugin
plugin.activate!
end
after do
Discourse.plugins.delete plugin
end
it "filters assets using the given path" do
get "/bootstrap.json"
expect(response.status).to eq(200)
plugin_assets = response.parsed_body.dig("bootstrap", "plugin_js")
expect(plugin_assets).not_to include(a_string_matching "my_plugin")
get "/bootstrap.json?for_url=/mypluginroute"
expect(response.status).to eq(200)
plugin_assets = response.parsed_body.dig("bootstrap", "plugin_js")
expect(plugin_assets).to include(a_string_matching "my_plugin")
end
end
end end