Skip to content

Commit c0d3a85

Browse files
committed
[BREAKING] Security: Disable JavaScript execution in Less.js
Execution of JavaScript code located in *.less files is unexpected in the context of OpenUI5 / SAPUI5 development and poses a security threat. BREAKING CHANGE: Parser option `javascriptEnabled` has been removed. JavaScript is always disabled and cannot be enabled.
1 parent 6db6f0a commit c0d3a85

File tree

10 files changed

+144
-75
lines changed

10 files changed

+144
-75
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,10 @@ lib2
149149
Type: `object`
150150

151151
Options for the [less](http://lesscss.org) parser (`less.Parser`).
152-
**Note:** Default of `relativeUrls` option is changed from `false` to `true`.
152+
153+
**Note**
154+
- Default of `relativeUrls` option is changed from `false` to `true`.
155+
- Option `javascriptEnabled` has been removed. JavaScript is always disabled and cannot be enabled.
153156

154157
##### compiler
155158

lib/thirdparty/less/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@
33
This folder contains the `lib/less` sub-folder of [v1.6.3](https://github.com/less/less.js/tree/v1.6.3/lib/less) of the [less.js project](https://github.com/less/less.js) with commit [`ccd8ebbfdfa300b6e748e8d7c12e3dbb0efd8371`](https://github.com/less/less.js/commit/ccd8ebbfdfa300b6e748e8d7c12e3dbb0efd8371) applied on top of it to resolve https://github.com/SAP/less-openui5/issues/24.
44

55
The files `browser.js` and `rhino.js` have been removed, as they are not relevant for the Node.js implementation.
6+
7+
The file `tree/javascript.js` has been removed to disable JavaScript execution.
8+
9+
Modifications within the files are marked with `/* BEGIN MODIFICATION */` and `/* END MODIFICATION */` comments.

lib/thirdparty/less/env.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
'compress', // option - whether to compress
1515
'processImports', // option - whether to process imports. if false then imports will not be imported
1616
'syncImport', // option - whether to import synchronously
17-
'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true
17+
/* BEGIN MODIFICATION */
18+
// Removed 'javascriptEnabled'
19+
/* END MODIFICATION */
1820
'mime', // browser only - mime type for sheet import
1921
'useFileCache', // browser only - whether to use the per file session cache
2022
'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc.

lib/thirdparty/less/functions.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,11 @@ tree.functions = {
213213
}
214214
},
215215
e: function (str) {
216-
return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str);
216+
/* BEGIN MODIFICATION */
217+
// Removed handling of tree.JavaScript
218+
return new(tree.Anonymous)(str);
219+
/* END MODIFICATION */
220+
217221
},
218222
escape: function (str) {
219223
return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29"));

lib/thirdparty/less/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,9 @@ require('./tree/mixin');
112112
require('./tree/comment');
113113
require('./tree/anonymous');
114114
require('./tree/value');
115-
require('./tree/javascript');
115+
/* BEGIN MODIFICATION */
116+
// Removed require('./tree/javascript');
117+
/* END MODIFICATION */
116118
require('./tree/assignment');
117119
require('./tree/condition');
118120
require('./tree/paren');

lib/thirdparty/less/lessc_helper.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ var lessc_helper = {
3131
console.log(" -M, --depends Output a makefile import dependency list to stdout");
3232
console.log(" --no-color Disable colorized output.");
3333
console.log(" --no-ie-compat Disable IE compatibility checks.");
34-
console.log(" --no-js Disable JavaScript in less files");
34+
/* BEGIN MODIFICATION */
35+
// Removed --no-js option
36+
/* END MODIFICATION */
3537
console.log(" -l, --lint Syntax check only (lint).");
3638
console.log(" -s, --silent Suppress output of error messages.");
3739
console.log(" --strict-imports Force evaluation of imports.");

lib/thirdparty/less/parser.js

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -993,27 +993,23 @@ less.Parser = function Parser(env) {
993993
}
994994
},
995995

996+
/* BEGIN MODIFICATION */
997+
// Removed support for javascript
998+
996999
//
997-
// JavaScript code to be evaluated
1000+
// JavaScript code (disabled)
9981001
//
9991002
// `window.location.href`
10001003
//
10011004
javascript: function () {
1002-
var str, j = i, e;
1005+
var j = i, e;
10031006

10041007
if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings
10051008
if (input.charAt(j) !== '`') { return; }
1006-
if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) {
1007-
error("You are using JavaScript, which has been disabled.");
1008-
}
1009-
1010-
if (e) { $char('~'); }
10111009

1012-
str = $re(/^`([^`]*)`/);
1013-
if (str) {
1014-
return new(tree.JavaScript)(str[1], i, e);
1015-
}
1010+
error("You are using JavaScript, which has been disabled.");
10161011
}
1012+
/* END MODIFICATION */
10171013
},
10181014

10191015
//

lib/thirdparty/less/tree/javascript.js

Lines changed: 0 additions & 58 deletions
This file was deleted.

lib/thirdparty/less/tree/quoted.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@ tree.Quoted.prototype = {
2222
eval: function (env) {
2323
var that = this;
2424
var value = this.value.replace(/`([^`]+)`/g, function (_, exp) {
25-
return new(tree.JavaScript)(exp, that.index, true).eval(env).value;
25+
/* BEGIN MODIFICATION */
26+
// Removed support for javascript
27+
const error = new Error("You are using JavaScript, which has been disabled.");
28+
error.index = that.index;
29+
error.type = "Syntax";
30+
throw error;
31+
/* END MODIFICATION */
2632
}).replace(/@\{([\w-]+)\}/g, function (_, name) {
2733
var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true);
2834
return (v instanceof tree.Quoted) ? v.value : v.toCSS();

test/test.js

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,114 @@ describe("error handling", function() {
419419
assert.ok(err);
420420
});
421421
});
422+
423+
it("should throw error when using inline JavaScript", function() {
424+
const lessInput = `.rule {
425+
@var: \`(function(){ return "Cat"; })()\`;
426+
color: @var;
427+
}`;
428+
return new Builder().build({
429+
lessInput
430+
}).then(function(res) {
431+
// no resolve
432+
assert.ok(false, `Expected build to fail but finished with content:\n${res.css}`);
433+
}, function(err) {
434+
assert.equal(err.message, "You are using JavaScript, which has been disabled.");
435+
assert.ok(err);
436+
});
437+
});
438+
439+
it("should throw error when using quoted inline JavaScript", function() {
440+
const lessInput = `.rule {
441+
@var: "\`(function(){ return 'Cat'; })()\`";
442+
color: @var;
443+
}`;
444+
return new Builder().build({
445+
lessInput
446+
}).then(function(res) {
447+
// no resolve
448+
assert.ok(false, `Expected build to fail but finished with content:\n${res.css}`);
449+
}, function(err) {
450+
assert.equal(err.message, "You are using JavaScript, which has been disabled.");
451+
assert.ok(err);
452+
});
453+
});
454+
455+
it("should throw error when using inline JavaScript with parser option javascriptEnabled: true", function() {
456+
const lessInput = `.rule {
457+
@var: \`(function(){ return "Cat"; })()\`;
458+
color: @var;
459+
}`;
460+
return new Builder().build({
461+
lessInput,
462+
parser: {
463+
javascriptEnabled: true
464+
}
465+
}).then(function(res) {
466+
// no resolve
467+
assert.ok(false, `Expected build to fail but finished with content:\n${res.css}`);
468+
}, function(err) {
469+
assert.equal(err.message, "You are using JavaScript, which has been disabled.");
470+
assert.ok(err);
471+
});
472+
});
473+
474+
it("should throw error when using quoted inline JavaScript with parser option javascriptEnabled: true", function() {
475+
const lessInput = `.rule {
476+
@var: "\`(function(){ return 'Cat'; })()\`";
477+
color: @var;
478+
}`;
479+
return new Builder().build({
480+
lessInput,
481+
parser: {
482+
javascriptEnabled: true
483+
}
484+
}).then(function(res) {
485+
// no resolve
486+
assert.ok(false, `Expected build to fail but finished with content:\n${res.css}`);
487+
}, function(err) {
488+
assert.equal(err.message, "You are using JavaScript, which has been disabled.");
489+
assert.ok(err);
490+
});
491+
});
492+
493+
it("should throw error when using inline JavaScript with parser option javascriptEnabled: false", function() {
494+
const lessInput = `.rule {
495+
@var: \`(function(){ return "Cat"; })()\`;
496+
color: @var;
497+
}`;
498+
return new Builder().build({
499+
lessInput,
500+
parser: {
501+
javascriptEnabled: false
502+
}
503+
}).then(function(res) {
504+
// no resolve
505+
assert.ok(false, `Expected build to fail but finished with content:\n${res.css}`);
506+
}, function(err) {
507+
assert.equal(err.message, "You are using JavaScript, which has been disabled.");
508+
assert.ok(err);
509+
});
510+
});
511+
512+
it("should throw error when using quoted inline JavaScript with parser option javascriptEnabled: false", function() {
513+
const lessInput = `.rule {
514+
@var: "\`(function(){ return 'Cat'; })()\`";
515+
color: @var;
516+
}`;
517+
return new Builder().build({
518+
lessInput,
519+
parser: {
520+
javascriptEnabled: false
521+
}
522+
}).then(function(res) {
523+
// no resolve
524+
assert.ok(false, `Expected build to fail but finished with content:\n${res.css}`);
525+
}, function(err) {
526+
assert.equal(err.message, "You are using JavaScript, which has been disabled.");
527+
assert.ok(err);
528+
});
529+
});
422530
});
423531

424532
function assertLessToRtlCssEqual(filename) {

0 commit comments

Comments
 (0)