Skip to content

Commit

Permalink
Merge branch 'refactorVersionHistory' of https://github.com/G-Ambatte…
Browse files Browse the repository at this point in the history
…/homebrewery into refactorVersionHistory
  • Loading branch information
G-Ambatte committed Oct 29, 2024
2 parents ebf900b + 0037c91 commit ea25ae6
Show file tree
Hide file tree
Showing 7 changed files with 819 additions and 318 deletions.
3 changes: 3 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ jobs:
- run:
name: Test - Routes
command: npm run test:route
- run:
name: Test - HTML sanitization
command: npm run test:safehtml
- run:
name: Test - Coverage
command: npm run test:coverage
Expand Down
26 changes: 13 additions & 13 deletions client/homebrew/brewRenderer/brewRenderer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ const Frame = require('react-frame-component').default;
const dedent = require('dedent-tabs').default;
const { printCurrentBrew } = require('../../../shared/helpers.js');

const DOMPurify = require('dompurify');
const purifyConfig = { FORCE_BODY: true, SANITIZE_DOM: false };
import { safeHTML } from './safeHTML.js';

const PAGE_HEIGHT = 1056;

Expand All @@ -29,14 +28,15 @@ const INITIAL_CONTENT = dedent`
<base target=_blank>
</head><body style='overflow: hidden'><div></div></body></html>`;


//v=====----------------------< Brew Page Component >---------------------=====v//
const BrewPage = (props)=>{
props = {
contents : '',
index : 0,
...props
};
const cleanText = props.contents; //DOMPurify.sanitize(props.contents, purifyConfig);
const cleanText = safeHTML(props.contents);
return <div className={props.className} id={`p${props.index + 1}`} >
<div className='columnWrapper' dangerouslySetInnerHTML={{ __html: cleanText }} />
</div>;
Expand Down Expand Up @@ -77,19 +77,19 @@ const BrewRenderer = (props)=>{
rawPages = props.text.split(/^\\page$/gm);
}

const scrollToHash = (hash) => {
if (!hash) return;
const scrollToHash = (hash)=>{
if(!hash) return;

const iframeDoc = document.getElementById('BrewRenderer').contentDocument;
let anchor = iframeDoc.querySelector(hash);

if (anchor) {
if(anchor) {
anchor.scrollIntoView({ behavior: 'smooth' });
} else {
// Use MutationObserver to wait for the element if it's not immediately available
new MutationObserver((mutations, obs) => {
anchor = iframeDoc.querySelector(hash);
if (anchor) {
new MutationObserver((mutations, obs)=>{
anchor = iframeDoc.querySelector(hash);
if(anchor) {
anchor.scrollIntoView({ behavior: 'smooth' });
obs.disconnect();
}
Expand Down Expand Up @@ -125,9 +125,9 @@ const BrewRenderer = (props)=>{
};

const renderStyle = ()=>{
const cleanStyle = props.style; //DOMPurify.sanitize(props.style, purifyConfig);
const themeStyles = props.themeBundle?.joinedStyles ?? '<style>@import url("/themes/V3/Blank/style.css");</style>';
return <div style={{ display: 'none' }} dangerouslySetInnerHTML={{ __html: `${themeStyles} \n\n <style> ${cleanStyle} </style>` }} />;
const cleanStyle = safeHTML(`${themeStyles} \n\n <style> ${props.style} </style>`);
return <div style={{ display: 'none' }} dangerouslySetInnerHTML={{ __html: cleanStyle }} />;
};

const renderPage = (pageText, index)=>{
Expand Down Expand Up @@ -201,8 +201,8 @@ const BrewRenderer = (props)=>{
styleObject.backgroundImage = `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='40px' width='200px'><text x='0' y='15' fill='%23fff7' font-size='20'>${global.config.deployment}</text></svg>")`;
}

const renderedStyle = useMemo(()=> renderStyle(), [props.style, props.themeBundle]);
renderedPages = useMemo(() => renderPages(), [props.text]);
const renderedStyle = useMemo(()=>renderStyle(), [props.style, props.themeBundle]);
renderedPages = useMemo(()=>renderPages(), [props.text]);

return (
<>
Expand Down
46 changes: 46 additions & 0 deletions client/homebrew/brewRenderer/safeHTML.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Derived from the vue-html-secure package, customized for Homebrewery

let doc = null;
let div = null;

function safeHTML(htmlString) {
// If the Document interface doesn't exist, exit
if(typeof document == 'undefined') return null;
// If the test document and div don't exist, create them
if(!doc) doc = document.implementation.createHTMLDocument('');
if(!div) div = doc.createElement('div');

// Set the test div contents to the evaluation string
div.innerHTML = htmlString;
// Grab all nodes from the test div
const elements = div.querySelectorAll('*');

// Blacklisted tags
const blacklistTags = ['script', 'noscript', 'noembed'];
// Tests to remove attributes
const blacklistAttrs = [
(test)=>{return test.localName.indexOf('on') == 0;},
(test)=>{return test.localName.indexOf('type') == 0 && test.value.match(/submit/i);},
(test)=>{return test.value.replace(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205f\u3000]/g, '').toLowerCase().trim().indexOf('javascript:') == 0;}
];


elements.forEach((element)=>{
// Check each element for blacklisted type
if(blacklistTags.includes(element?.localName?.toLowerCase())) {
element.remove();
return;
}
// Check remaining elements for blacklisted attributes
for (const attribute of element.attributes){
if(blacklistAttrs.some((test)=>{return test(attribute);})) {
element.removeAttribute(attribute.localName);
break;
};
};
});

return div.innerHTML;
};

module.exports.safeHTML = safeHTML;
Loading

0 comments on commit ea25ae6

Please sign in to comment.