AJAX loaded xml and xsl then transform it to html. The result node is imported to html container. The result has script element with inline javascript code.
After Chrome update inline code won't executed.
For example:
HTML
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<script>
let buttonElement = document.createElement("button");
buttonElement.textContent = "Press me";
document.body.appendChild(buttonElement);
buttonElement.addEventListener("click", event => {
Promise.all([fetch('/test.xml'), fetch('/test.xsl')]).then(result => {
Promise.all([result[0].text(), result[1].text()]).then(result => {
let parser = new DOMParser(),
xml = parser.parseFromString(result[0], "application/xml"),
xsl = parser.parseFromString(result[1], "application/xml"),
target = document.body,
processor = new XSLTProcessor(),
output;
processor.importStylesheet(xsl);
output = processor.transformToDocument(xml);
let newOutput = document.body.appendChild(output.documentElement);
/*
Uncomment the block below, the result node its replacement helps and inline script will be executed properly
*/
/* newOutput.querySelectorAll("script").forEach(element => {
let replacement = document.createElement("script");
replacement.text = element.text;
element.replaceWith(replacement);
});
*/
});
});
}, false);
</script>
</body>
</html>
XML
<?xml version="1.0" encoding="UTF-8"?>
<hello>
<world/>
</hello>
XSL
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
<xsl:output method="xml" indent="no"/>
<xsl:template match="/">
<div>
<p>Hello world</p>
<script>console.log("Test me!");</script>
</div>
</xsl:template>
</xsl:stylesheet>
So, is it security bug of Webkit? Because, this bug was also detected in Opera.
And if this is security solution, why inline script is succesfully executed after result node replacement?
Related
I am trying to do XSL transformation in Edge and using a document node as xslt parameter(via XSLTProcessor.setParameter).
The transformToFragment returns null in Edge (and Chrome), but works ok in Firefox.
Is there a way to make the code below working (at least in Edge) or is there no support of document-node as a xslt parameter?
Below is a minimal code to present the issue.
https://jsfiddle.net/BennyHilarious/nxzLf9g3/1/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<script type="text/javascript">
function example() {
let xsldocp = new DOMParser();
const xsldoc = xsldocp.parseFromString(`<?xml version='1.0'?>
<xsl:stylesheet version='1.0'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
xmlns="http://www.w3.org/1999/xhtml">
<xsl:output method='xml' omit-xml-declaration='yes'/>
<!-- Parameters -->
<xsl:param name='ntParam'/>
<xsl:param name='strSpaces' select='" "'/> <!-- Default to 4 spaces -->
<!-- Global variables -->
<xsl:variable name='numTransformLevel' select='$ntParam/PARAMETER/#VALUE'/> <!-- does not work in Edge & Chrome -->
<!--xsl:variable name='numTransformLevel' select='2'/--><!-- works OK -->
<!-- Handles a generic element node. -->
<xsl:template match='*'>
<div>
<b><xsl:value-of select='$numTransformLevel'/></b>
<i><xsl:value-of select='//GROOT/#VALUE'/></i>
</div>
</xsl:template>
</xsl:stylesheet>
`,"application/xml");
let xsltProcessor = new XSLTProcessor();
// use the xsl sheet
xsltProcessor.importStylesheet(xsldoc);
// create xml
let parser = new DOMParser();
const docData = parser.parseFromString("<ROOT><GROOT VALUE='IAM'/></ROOT>", "application/xml");
// create param xml and set the parameter
parser = new DOMParser();
const docParam = parser.parseFromString("<PARAM><PARAMETER VALUE='5'/></PARAM>", "application/xml");
xsltProcessor.setParameter(null, "ntParam", docParam.documentElement);
// do the transformation
var targetElm = document.getElementById("trCont5");
let resultFrag = xsltProcessor.transformToFragment(docData, targetElm.ownerDocument);
targetElm.appendChild( resultFrag );
}
</script>
</head>
<body>
<input type="button" onclick="example();" value="XLSTParam Test"/><!-- Outputs 5IAM -->
<div id="trCont5"></div>
</body>
</html>
Using SaxonJS (docs at https://www.saxonica.com/saxon-js/documentation2/index.html, download at https://www.saxonica.com/download/javascript.xml):
function example() {
const xslt = `<?xml version='1.0'?>
<xsl:stylesheet version='1.0'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
xmlns="http://www.w3.org/1999/xhtml">
<xsl:output method='html'/>
<!-- Parameters -->
<xsl:param name='ntParam'/>
<xsl:param name='strSpaces' select='" "'/> <!-- Default to 4 spaces -->
<!-- Global variables -->
<xsl:variable name='numTransformLevel' select='$ntParam/PARAMETER/#VALUE'/> <!-- does not work in Edge & Chrome -->
<!--xsl:variable name='numTransformLevel' select='2'/--><!-- works OK -->
<!-- Handles a generic element node. -->
<xsl:template match='*'>
<div>
<b><xsl:value-of select='$numTransformLevel'/></b>
<i><xsl:value-of select='//GROOT/#VALUE'/></i>
</div>
</xsl:template>
</xsl:stylesheet>
`;
// create xml
let parser = new DOMParser();
const docData = parser.parseFromString("<ROOT><GROOT VALUE='IAM'/></ROOT>", "application/xml");
// create param xml and set the parameter
const docParam = parser.parseFromString("<PARAM><PARAMETER VALUE='5'/></PARAM>", "application/xml");
// do the transformation
var targetElm = document.getElementById("trCont5");
let result = SaxonJS.XPath.evaluate(`
transform(map {
'source-node' : .,
'stylesheet-text' : $xslt,
'stylesheet-params' : map {
QName('', 'ntParam') : $ntParam
}
})?output`,
docData,
{
'params' : {
ntParam : docParam.documentElement,
xslt : xslt
}
});
targetElm.appendChild( result );
}
</script>
</head>
<body>
<input type="button" onclick="example();" value="XLSTParam Test"/><!-- Outputs 5IAM -->
<div id="trCont5"></div>
</body>
<script src="https://martin-honnen.github.io/xslt3fiddle/js/SaxonJS2.js"></script>
<input type="button" onclick="example();" value="XSLTParam Test"/><!-- Outputs 5IAM -->
<div id="trCont5"></div>
Note that for compactness and self-containedness of the example I used the SaxonJS.XPath.evaluatemethod to call the XPath 3.1 transform function on the fly to run the provided XSLT. SaxonJS also allows you to run precompiled XSLT with the SaxonJS.transform method, for that you need to use either SaxonEE or the Node.js xslt3 tool (from Saxonica/NPM https://www.npmjs.com/package/xslt3) to compile the XSLT file to a JSON file.
I write an application where AJAX loads an XSLT which has a <script> inside.
The strange thing is that script runs in Firefox without eval(). Script is not ran in Opera or Chrome.
So, I simplify the code and I post it here.
Is it a Firefox bug?
Is there a cross-browser workaround? (A workaround which does not run eval() on firefox, only for scripts from xslt, but run eval() on Chrome, Opera for every script loaded with AJAX - both in XSLT or in XHTML).
index.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head><script src="app.js" /></head>
<body onload="locationHashChanged();" />
</html>
app.js
function get(file) {
var request = new XMLHttpRequest();
request.open("GET", file, false);
request.send();
return request.responseXML;
}
function insert(where, root) {
//var scripts = root.getElementsByTagName('script');
where.parentNode.replaceChild(root, where);
//for(var z = 0; z != scripts.length; z++) eval(scripts[z]);
}
function locationHashChanged() {
var xml = get('apps.xml');
var xslt = get('xslt.xsl');
var xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(xslt);
insert(document.body, xsltProcessor.transformToFragment(xml, document).firstChild);
};
xslt.xsl
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*" />
<xsl:template match="//*[#path='']">
<body xmlns="http://www.w3.org/1999/xhtml">
<script>alert('Only in Firefox');</script>
</body>
</xsl:template>
</xsl:stylesheet>
apps.xml
<?xml version="1.0" encoding="UTF-8"?>
<menu name="" path="" />
Would it help to put the alert in a CDATA tag? So
<script type="text/javascript">
<![CDATA[alert('Only in Firefox');]]>
</script>
I've started doing that for all javascript that I include in xslt templates
I am trying to generate output like this through XSL:
<script>
var quickCatalogueCategories = {
for each values do {
'{title}': 'http://www.news.com/{search key}',
}
};
</script>
The problem is I am getting the output but after each title I should get url instead after all titles I am getting the URL. Even after the URL I should have only one key but continuously displaying all four keys.
Can you tell me how to fix it?
Trying to achieve output like this:
<script>
var quickCatalogueCategories = {
'{What's New}':'http: //www.news.com/{news}',
'{Featured}':'http: //www.news.com/{featured}',
'{Most Delivered}':'http: //www.news.com/{most-delivered}',
'{Online Only}':'http: //www.news.com/{online-only}',
}
</script>
try this code
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Skin: Default XSL -->
<!--xsl:include href="http://www.interwoven.com/livesite/xsl/HTMLTemplates.xsl"/-->
<!--xsl:include href="http://www.interwoven.com/livesite/xsl/StringTemplates.xsl"/-->
<xsl:template match="/">
<!-- TODO put section -->
<script>
var quickCatalogueCategories={
<xsl:for-each select="//Datum[#Name='Catalog']//CatalogContainer">
<!-- you are already in the node, just select the value of the children -->
<xsl:text> '{</xsl:text><xsl:value-of select="CatalogKey"/><xsl:text>'}</xsl:text>:'http://www.news.com/{'<xsl:value-of select="CatalogView"/>}',}
</xsl:for-each>
}
</script>
</xsl:template>
</xsl:stylesheet>
I saw a lot of posts online about MSXML4 to 6 or XSLT 1.0 versus 2.0 etc. But they could not answer my question.
I have a XSLT transformation code that works with MSXML4 APIs (XSLTransform and FreeThreadedDomDocument) on IE7 via Javascript.
Same code doesnt work with with MSXML6 APIs (XSLTransform and DomDocument) on IE9 via Javascript. It throws this error
"Namespace 'urn:mynamespace:mytable:transactions' does not contain any functions"
I have made sure that my ActiveX are enabled for MSXML4 and 6 both on IE9. Below is the code of the main tranformer XSLT, the reference XSLT & the JS code ...
Core XSLT: functions.xsl
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:myfuncs="urn:mynamespace:mytable:transactions" >
<msxsl:script language="javascript" implements-prefix="myfuncs">
<![CDATA[
// convert system GMT time into local time
// usage: <xsl:value-of select="myfuncs:localDateTime(datetime)"/>
var openBalance = 0;
function setOpenBalance(openBal)
{
openBalance = openBal;
}
function getOpenBalance()
{
openBalance = openBal;
return openBalance ;
}
]]>
</msxsl:script>
</xsl:stylesheet>
Main XSLT: MyTransformer.xsl ... that refers functions.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:myfuncs="urn:mynamespace:mytable:transactions">
<xsl:output method="xml"/>
<xsl:include href="functions.xsl" />
<!--<xsl:variable name="trade_cur_bal" select="myfuncs:getOpenBalance(100)"/>-->
<xsl:template match="/">
<Response>
<!-- Some working code here -->
</Response>
</xsl:template>
</xsl:stylesheet>
JS Code
var domXsl = new ActiveXObject("Msxml2.FreeThreadedDOMDocument.4.0");
/*
// In case of IE9 ....
var domXsl = new ActiveXObject("Msxml2.FreeThreadedDOMDocument.6.0");
*/
var domHTML = new ActiveXObject("Msxml2.XSLTemplate.4.0");
/*
// In case of IE9 ....
var domHTML = new ActiveXObject("Msxml2.XSLTemplate.6.0");
*/
domXsl.async=false;
domXsl.load("MyTransformer.xsl");
domHTML.stylesheet = domXsl;
var domData = new ActiveXObject("Msxml2.FreeThreadedDOMDocument.4.0");
var input = "<MyInputData></MyInputData>"
domData.loadXML(input);
var result = tranform(domHTML, domData); //Works for MSXML 4.0 and fails for MSXML 6.0
function transform(template_, input_) {
var output = "";
if (input_ != null && input_.xml != "") {
var proc = template_.createProcessor();
proc.input = input_;
proc.transform();
output = proc.output;
delete proc;
}
return output;
}
Can someone guide me where am I going wrong w.r.t. MSXML6 or IE9?
Thx.
With MSXML 6 use of script inside XSLT is disabled by default for security reasons, so you need to explicitly enable it by calling
var domXsl = new ActiveXObject("Msxml2.FreeThreadedDOMDocument.6.0");
domXsl.setProperty("AllowXsltScript", true);
And additionally to allow the use of xsl:import or xsl:include you also need to set
domXsl.setProperty("ResolveExternals", true);
I got this fixed by removing the versions (4 and 6) form the activeX class ID
e.g. new ActiveXObject("Msxml2.FreeThreadedDomDocument") etc.
i want to run xsl file using javascript function. I wrote a javascrpt function which is working well with Firefox and Crom but it is not working on Internet Explorer
function loadXMLDoc(dname)
{
if (window.XMLHttpRequest)
{
xhttp=new XMLHttpRequest();
}
else
{
xhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xhttp.open("GET",dname,false);
xhttp.send("");
return xhttp.responseXML;
}
function displayResult()
{
xml=loadXMLDoc("NewXml.xml");
xsl=loadXMLDoc("NewFile.xsl");
// code for IE
if (window.ActiveXObject)
{
ex=xml.transformNode(xsl);
document.getElementById("example").innerHTML=ex;
}
// code for Mozilla, Firefox, Opera, etc.
else if (document.implementation && document.implementation.createDocument)
{
xsltProcessor=new XSLTProcessor();
xsltProcessor.importStylesheet(xsl);
resultDocument = xsltProcessor.transformToFragment(xml,document);
document.getElementById("example").appendChild(resultDocument);
}
}
Please help my by modifying this code or by another code so that i can work with Internet Explorer.
Thanks
To apply xsl trasformation I use, and promote, Sarissa. It is a crossbrowser library that envelope the xml apis of the different browser.
For an example of a trasformation with Sarissa you can head straight to the howto page
but it is similar to this:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>sarissa test1</title>
<script type="text/javascript" src="sarissa.js"></script>
</head>
<body>
<script type="text/javascript">
function loadXMLDoc(dname) {
xhttp = new XMLHttpRequest();
xhttp.open("GET",dname,false);
xhttp.send("");
return xhttp.responseXML;
}
var processor = new XSLTProcessor();
var theXML = loadXMLDoc('xml.xml');
var theXSL = loadXMLDoc('xsl.xsl');
// prepare the processor
processor.importStylesheet(theXSL);
var theResult = processor.transformToDocument(theXML);
// now you have a DomDocument with the result
// if you want to serialize (transform to a string) it you van use
document.write(new XMLSerializer().serializeToString(theResult));
</script>
</body>
</html>
Quick, easy, mantained and you can focus your efforts to the xsl creation instead than struggling to overcome the browsers differences.
The single glitch, to me is a plus but someone may see it as a limitation, the code is released under GPL 2.1 or higher or, if you prefer Apache Licence 2.0 or higher
Edit: Mine fault, I posted an old (and wrong code) without checking it. Created a new version miming your script (and tested it) on firefox, chrome, ie8, ie7 and it worked flawlessy. I used the first two xsl/xml found on a google search (I report them below for
completeness). Try the code as is and with your xsl/xml. If it goes wrong report the malfunction too (we can be more effective with a deeper error description than - don't work - ). What happens when the code misbehave? (the browser freeze, report an error, return a blank result)
xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--?xml-stylesheet type="text/xsl" href="xsl.xsl"?-->
<tutorials>
<tutorial>
<name>XML Tutorial</name>
<url>http://www.quackit.com/xml/tutorial</url>
</tutorial>
<tutorial>
<name>HTML Tutorial</name>
<url>http://www.quackit.com/html/tutorial</url>
</tutorial>
</tutorials>
xsl:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- try to enable/disable the xsl:output -->
<!-- xsl:output method="xml"
version="1.0"
encoding="iso-8859-1"
indent="yes"/-->
<xsl:template match="/">
<html>
<head>
<title>XML XSL Example</title>
<style type="text/css">
body
{
margin:10px;
background-color:#ccff00;
font-family:verdana,helvetica,sans-serif;
}
.tutorial-name
{
display:block;
font-weight:bold;
}
.tutorial-url
{
display:block;
color:#636363;
font-size:small;
font-style:italic;
}
</style>
</head>
<body>
<h2>Cool Tutorials</h2>
<p>Hey, check out these tutorials!</p>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="tutorial">
<span class="tutorial-name"><xsl:value-of select="name"/></span>
<span class="tutorial-url"><xsl:value-of select="url"/></span>
</xsl:template>
</xsl:stylesheet>