Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .meteor/packages
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,4 @@ rocketchat:i18n
rocketchat:postcss
dandv:caret-position
facts-base@1.0.1
browser-policy
4 changes: 4 additions & 0 deletions .meteor/versions
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ blaze@2.3.4
blaze-html-templates@1.1.2
blaze-tools@1.0.10
boilerplate-generator@1.7.1
browser-policy@1.1.0
browser-policy-common@1.0.11
browser-policy-content@1.1.1
browser-policy-framing@1.1.0
caching-compiler@1.2.2
caching-html-compiler@1.1.3
callback-hook@1.3.0
Expand Down
6 changes: 6 additions & 0 deletions app/lib/server/startup/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,12 @@ settings.addGroup('General', function() {
type: 'boolean',
secret: true,
});

this.add('Enable_CSP', true, {
type: 'boolean',
secret: true,
Comment thread
ggazzo marked this conversation as resolved.
Outdated
});

this.add('Iframe_Restrict_Access', true, {
type: 'boolean',
secret: true,
Expand Down
5 changes: 2 additions & 3 deletions app/theme/server/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import { Meteor } from 'meteor/meteor';

import { settings } from '../../settings';
import { Logger } from '../../logger';
import { getURL } from '../../utils/lib/getURL';
import { injectIntoHead } from '../../ui-master/server';
import { addStyle } from '../../ui-master/server/inject';

const logger = new Logger('rocketchat:theme', {
methods: {
Expand Down Expand Up @@ -140,7 +139,7 @@ Meteor.startup(() => {
settings.get('css', (key, value = '') => {
currentHash = crypto.createHash('sha1').update(value).digest('hex');
currentSize = value.length;
injectIntoHead('css-theme', `<link rel="stylesheet" type="text/css" href="${ getURL(`/theme.css?${ currentHash }`) }">`);
addStyle('css-theme', value);
});
});

Expand Down
23 changes: 4 additions & 19 deletions app/ui-master/client/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,12 @@ import { CachedCollectionManager } from '../../ui-cached-collection';
import { tooltip } from '../../ui/client/components/tooltip';
import { callbacks } from '../../callbacks/client';
import { isSyncReady } from '../../../client/lib/userData';
import { fireGlobalEvent } from '../../ui-utils/client';

import './main.html';

function executeCustomScript(script) {
eval(script);//eslint-disable-line
}

function customScriptsOnLogout() {
const script = settings.get('Custom_Script_On_Logout') || '';
if (script.trim()) {
executeCustomScript(script);
}
}

callbacks.add('afterLogoutCleanUp', () => customScriptsOnLogout(), callbacks.priority.LOW, 'custom-script-on-logout');
callbacks.add('afterLogoutCleanUp', () => fireGlobalEvent('Custom_Script_On_Logout'), callbacks.priority.LOW, 'custom-script-on-logout');

Template.main.helpers({
removeSidenav: () => Layout.isEmbedded() && !/^\/admin/.test(FlowRouter.current().route.path),
Expand Down Expand Up @@ -81,16 +72,10 @@ Template.main.helpers({
return mandatoryRole !== undefined && is2faEnabled;
},
CustomScriptLoggedOut: () => {
const script = settings.get('Custom_Script_Logged_Out') || '';
if (script.trim()) {
executeCustomScript(script);
}
fireGlobalEvent('Custom_Script_Logged_Out');
},
CustomScriptLoggedIn: () => {
const script = settings.get('Custom_Script_Logged_In') || '';
if (script.trim()) {
executeCustomScript(script);
}
fireGlobalEvent('Custom_Script_Logged_In');
},
embeddedVersion: () => {
if (Layout.isEmbedded()) {
Expand Down
166 changes: 166 additions & 0 deletions app/ui-master/server/index.js
Original file line number Diff line number Diff line change
@@ -1 +1,167 @@
import { Meteor } from 'meteor/meteor';
import { BrowserPolicy } from 'meteor/browser-policy';
import { Inject } from 'meteor/meteorhacks:inject-initial';
import { Tracker } from 'meteor/tracker';
import _ from 'underscore';
import { escapeHTML } from '@rocket.chat/string-helpers';

import { Settings } from '../../models';
import { settings } from '../../settings/server';
import { applyHeadInjections, headInjections, injectIntoBody, injectIntoHead } from './inject';
import './scripts';

export * from './inject';

Meteor.startup(() => {
settings.get('Enable_CSP', (_, enabled) => {
if (!enabled) {
return BrowserPolicy.content.setPolicy("default-src 'self'; "
+ "script-src 'self' 'unsafe-inline' 'unsafe-eval'; "
+ 'connect-src * data:; '
+ 'img-src * data: ; '
+ "style-src 'self' 'unsafe-inline';");
}
BrowserPolicy.content.allowImageOrigin('*');
BrowserPolicy.content.disallowInlineScripts();
BrowserPolicy.content.allowFontDataUrl();
BrowserPolicy.content.allowConnectDataUrl();
BrowserPolicy.content.allowInlineStyles();
});
Tracker.autorun(() => {
const injections = Object.values(headInjections.all());
Inject.rawModHtml('headInjections', applyHeadInjections(injections));
});

settings.get('Default_Referrer_Policy', (key, value) => {
if (!value) {
return injectIntoHead('noreferrer', '<meta name="referrer" content="same-origin" />');
}

injectIntoHead('noreferrer', `<meta name="referrer" content="${ value }" />`);
});

if (process.env.DISABLE_ANIMATION) {
injectIntoHead('disable-animation', `
<style>
body, body * {
animation: none !important;
}
</style>
`);
}

settings.get('Assets_SvgFavicon_Enable', (key, value) => {
const standardFavicons = `
<link rel="icon" sizes="16x16" type="image/png" href="assets/favicon_16.png" />
<link rel="icon" sizes="32x32" type="image/png" href="assets/favicon_32.png" />`;

if (value) {
injectIntoHead(key,
`${ standardFavicons }
<link rel="icon" sizes="any" type="image/svg+xml" href="assets/favicon.svg" />`);
} else {
injectIntoHead(key, standardFavicons);
}
});

settings.get('theme-color-sidebar-background', (key, value) => {
const escapedValue = escapeHTML(value);
injectIntoHead(key, `<meta name="msapplication-TileColor" content="${ escapedValue }" />`
+ `<meta name="theme-color" content="${ escapedValue }" />`);
});

settings.get('Site_Name', (key, value = 'Rocket.Chat') => {
const escapedValue = escapeHTML(value);
injectIntoHead(key,
`<title>${ escapedValue }</title>`
+ `<meta name="application-name" content="${ escapedValue }">`
+ `<meta name="apple-mobile-web-app-title" content="${ escapedValue }">`);
});

settings.get('Meta_language', (key, value = '') => {
const escapedValue = escapeHTML(value);
injectIntoHead(key,
`<meta http-equiv="content-language" content="${ escapedValue }">`
+ `<meta name="language" content="${ escapedValue }">`);
});

settings.get('Meta_robots', (key, value = '') => {
const escapedValue = escapeHTML(value);
injectIntoHead(key, `<meta name="robots" content="${ escapedValue }">`);
});

settings.get('Meta_msvalidate01', (key, value = '') => {
const escapedValue = escapeHTML(value);
injectIntoHead(key, `<meta name="msvalidate.01" content="${ escapedValue }">`);
});

settings.get('Meta_google-site-verification', (key, value = '') => {
const escapedValue = escapeHTML(value);
injectIntoHead(key, `<meta name="google-site-verification" content="${ escapedValue }">`);
});

settings.get('Meta_fb_app_id', (key, value = '') => {
const escapedValue = escapeHTML(value);
injectIntoHead(key, `<meta property="fb:app_id" content="${ escapedValue }">`);
});

settings.get('Meta_custom', (key, value = '') => {
injectIntoHead(key, value);
});

const baseUrl = ((prefix) => {
if (!prefix) {
return '/';
}

prefix = prefix.trim();

if (!prefix) {
return '/';
}

return /\/$/.test(prefix) ? prefix : `${ prefix }/`;
})(__meteor_runtime_config__.ROOT_URL_PATH_PREFIX);

injectIntoHead('base', `<base href="${ baseUrl }">`);

injectIntoHead('css-theme', '');
});

const renderDynamicCssList = _.debounce(Meteor.bindEnvironment(() => {
// const variables = RocketChat.models.Settings.findOne({_id:'theme-custom-variables'}, {fields: { value: 1}});
const colors = Settings.find({ _id: /theme-color-rc/i }, { fields: { value: 1, editor: 1 } }).fetch().filter((color) => color && color.value);

if (!colors) {
return;
}
const css = colors.map(({ _id, value, editor }) => {
if (editor === 'expression') {
return `--${ _id.replace('theme-color-', '') }: var(--${ value });`;
}
return `--${ _id.replace('theme-color-', '') }: ${ value };`;
}).join('\n');
injectIntoBody('dynamic-variables', `<style id='css-variables'> :root {${ css }}</style>`);
}), 500);

renderDynamicCssList();

// RocketChat.models.Settings.find({_id:'theme-custom-variables'}, {fields: { value: 1}}).observe({
// changed: renderDynamicCssList
// });

settings.get(/theme-color-rc/i, () => renderDynamicCssList());

injectIntoBody('react-root', `
<div id="react-root">
<div class="page-loading">
<div class="loading-animation">
<div class="bounce bounce1"></div>
<div class="bounce bounce2"></div>
<div class="bounce bounce3"></div>
</div>
</div>
</div>
`);

injectIntoBody('icons', Assets.getText('public/icons.svg'));
Loading