mirror of
https://gitee.com/kekingcn/file-online-preview.git
synced 2026-09-13 00:14:56 +00:00
Merge commit from fork
Sandbox HTML preview execution
This commit is contained in:
@@ -469,8 +469,8 @@ kk.xlsxshowtoolbar = false
|
|||||||
# 首页是否显示key密钥 默认为false(禁用)
|
# 首页是否显示key密钥 默认为false(禁用)
|
||||||
kk.isshowkey = false
|
kk.isshowkey = false
|
||||||
|
|
||||||
# 预览html文件 是否启用JavaScript 默认为true(启用)
|
# 预览html文件 是否在隔离沙箱中启用JavaScript,默认为false(禁用)
|
||||||
kk.scriptjs = true
|
kk.scriptjs = false
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|||||||
@@ -466,8 +466,8 @@ kk.xlsxshowtoolbar = true
|
|||||||
# 首页是否显示key密钥 默认为false(禁用)
|
# 首页是否显示key密钥 默认为false(禁用)
|
||||||
kk.isshowkey = true
|
kk.isshowkey = true
|
||||||
|
|
||||||
# 预览html文件 是否启用JavaScript 默认为true(启用)
|
# 预览html文件 是否在隔离沙箱中启用JavaScript,默认为false(禁用)
|
||||||
kk.scriptjs = true
|
kk.scriptjs = false
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|||||||
@@ -16,6 +16,22 @@
|
|||||||
<script src="js/jsformat.js" type="text/javascript"></script>
|
<script src="js/jsformat.js" type="text/javascript"></script>
|
||||||
</#if>
|
</#if>
|
||||||
<script src="js/base64.min.js" type="text/javascript"></script>
|
<script src="js/base64.min.js" type="text/javascript"></script>
|
||||||
|
<style>
|
||||||
|
#htmlPreviewFrame {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 65vh;
|
||||||
|
border: 0;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
#htmlSource {
|
||||||
|
min-height: 65vh;
|
||||||
|
overflow: auto;
|
||||||
|
border: 0;
|
||||||
|
background: #fff;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<input hidden id="textData" value="${textData}"/>
|
<input hidden id="textData" value="${textData}"/>
|
||||||
@@ -25,7 +41,7 @@
|
|||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<h4 class="panel-title">
|
<h4 class="panel-title">
|
||||||
<strong><font color="red"><input class="GLOkBtn" type="button" value="运行html" onclick="loadXmlData();" /></font></strong>
|
<strong><font color="red"><input class="GLOkBtn" type="button" value="在沙箱中运行html" onclick="loadXmlData();" /></font></strong>
|
||||||
<a data-toggle="collapse" data-parent="#accordion" onclick="loadText();">
|
<a data-toggle="collapse" data-parent="#accordion" onclick="loadText();">
|
||||||
${file.name}
|
${file.name}
|
||||||
</a>
|
</a>
|
||||||
@@ -40,57 +56,42 @@
|
|||||||
// 将Freemarker的布尔值传递给JavaScript
|
// 将Freemarker的布尔值传递给JavaScript
|
||||||
var scriptjs = ${scriptjs?c}; // ?c 将布尔值转换为字符串true/false
|
var scriptjs = ${scriptjs?c}; // ?c 将布尔值转换为字符串true/false
|
||||||
|
|
||||||
|
function decodePreviewText() {
|
||||||
|
var escapedText = Base64.decode($("#textData").val());
|
||||||
|
var decoder = document.createElement("textarea");
|
||||||
|
decoder.innerHTML = escapedText;
|
||||||
|
return decoder.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function replacePreviewContent(element) {
|
||||||
|
var container = document.getElementById("text");
|
||||||
|
while (container.firstChild) {
|
||||||
|
container.removeChild(container.firstChild);
|
||||||
|
}
|
||||||
|
container.appendChild(element);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*加载普通文本
|
*加载普通文本
|
||||||
*/
|
*/
|
||||||
function loadText() {
|
function loadText() {
|
||||||
var base64data = $("#textData").val()
|
var source = document.createElement("pre");
|
||||||
var div = document.getElementById("text");
|
source.id = "htmlSource";
|
||||||
div.innerHTML = ""; //
|
source.textContent = decodePreviewText();
|
||||||
var textData = Base64.decode(base64data);
|
replacePreviewContent(source);
|
||||||
textData = htmlttt(textData,1);
|
|
||||||
var textPreData = "<xmp style='background-color: #FFFFFF;overflow-y: scroll;border:none'>" + textData + "</xmp>";
|
|
||||||
$("#text").append(textPreData);
|
|
||||||
}
|
|
||||||
|
|
||||||
function htmlttt (str,txt){
|
|
||||||
var s = "";
|
|
||||||
if(str.length == 0) return "";
|
|
||||||
s = str.replace(/&/gi,"&");
|
|
||||||
s = s.replace(/</gi,"<");
|
|
||||||
s = s.replace(/>/gi,">");
|
|
||||||
s = s.replace(/ /gi," ");
|
|
||||||
s = s.replace(/'/gi,"\'");
|
|
||||||
s = s.replace(/"/gi,"\"");
|
|
||||||
s = s.replace(/javascript/g,"javascript ");
|
|
||||||
if (txt === 2){
|
|
||||||
s = s.replace(/<script/gi, "<script ");
|
|
||||||
s = s.replace(/javascript/g,"javascript ");
|
|
||||||
s = s.replace(/<\/script/gi, "</script ");
|
|
||||||
s = s.replace(/<iframe/gi, "<iframe ");
|
|
||||||
s = s.replace(/<\/iframe/gi, "</iframe ");
|
|
||||||
s = s.replace(/confirm/gi, "c&onfirm");
|
|
||||||
s = s.replace(/alert/gi, "a&lert");
|
|
||||||
s = s.replace(/eval/gi, "e&val");
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*加载运行
|
*加载运行
|
||||||
*/
|
*/
|
||||||
function loadXmlData() {
|
function loadXmlData() {
|
||||||
var base64data = $("#textData").val();
|
var frame = document.createElement("iframe");
|
||||||
var textData = Base64.decode(base64data);
|
frame.id = "htmlPreviewFrame";
|
||||||
|
frame.title = "HTML sandbox preview";
|
||||||
// 直接使用JavaScript变量进行判断
|
frame.setAttribute("sandbox", scriptjs ? "allow-scripts" : "");
|
||||||
if (scriptjs) {
|
frame.setAttribute("referrerpolicy", "no-referrer");
|
||||||
textData = htmlttt(textData, 1);
|
frame.srcdoc = decodePreviewText();
|
||||||
} else {
|
replacePreviewContent(frame);
|
||||||
textData = htmlttt(textData, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#text').html(textData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package cn.keking.web;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
class HtmlPreviewSandboxTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRenderHtmlOnlyInsideAnOpaqueOriginSandbox() throws IOException {
|
||||||
|
String template = readResource("web/code.ftl");
|
||||||
|
|
||||||
|
assertTrue(template.contains("frame.setAttribute(\"sandbox\", scriptjs ? \"allow-scripts\" : \"\")"));
|
||||||
|
assertTrue(template.contains("frame.srcdoc = decodePreviewText()"));
|
||||||
|
assertFalse(template.contains("allow-same-origin"));
|
||||||
|
assertFalse(template.contains("$('#text').html(textData)"));
|
||||||
|
assertFalse(template.contains("function htmlttt"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldDisplaySourceAsTextAndDisableScriptsByDefault() throws IOException {
|
||||||
|
String template = readResource("web/code.ftl");
|
||||||
|
String properties = readResource("application.properties");
|
||||||
|
|
||||||
|
assertTrue(template.contains("source.textContent = decodePreviewText()"));
|
||||||
|
assertTrue(properties.contains("kk.scriptjs = false"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String readResource(String path) throws IOException {
|
||||||
|
ClassPathResource resource = new ClassPathResource(path);
|
||||||
|
return new String(resource.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user