VYPR
High severityNVD Advisory· Published Feb 16, 2021· Updated Aug 3, 2024

Arbitrary code execution in less-openui5

CVE-2021-21316

Description

less-openui5 before 0.10.0 executes JavaScript embedded in untrusted Less theme files, enabling code injection during builds.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

less-openui5 before 0.10.0 executes JavaScript embedded in untrusted Less theme files, enabling code injection during builds.

## Vulnerability less-openui5 is an npm package used to build OpenUI5 themes with Less.js. Prior to version 0.10.0, it used a fork of Less.js v1.6.3 that enabled inline JavaScript evaluation. When processing untrusted .less files, any embedded JavaScript code would be executed during the build process [1][2]. This behavior, while a feature of older Less.js versions, was unexpected in OpenUI5/SAPUI5 development.

Exploitation

An attacker could craft a malicious library or theme containing a .less file with hidden JavaScript, such as backtick-delimited code. If a developer or CI system uses less-openui5 to process this resource (e.g., via UI5 Tooling), the JavaScript runs with the privileges of the build process [2]. No authentication is required beyond access to the build pipeline.

Impact

Successful exploitation allows arbitrary code execution within the build environment. An attacker could steal credentials, modify source code, or pivot to internal systems. The vulnerability is particularly severe in CI/CD contexts where many developers share build resources.

Mitigation

The fix was released in less-openui5 version 0.10.0, which completely removes inline JavaScript evaluation from the Less.js fork [3][4]. Users should update to this version immediately. There is no workaround other than avoiding processing untrusted .less files.

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
less-openui5npm
< 0.10.00.10.0

Affected products

2

Patches

1
c0d3a8572974

[BREAKING] Security: Disable JavaScript execution in Less.js

https://github.com/SAP/less-openui5Matthias OßwaldJan 29, 2021via ghsa
10 files changed · +144 75
  • lib/thirdparty/less/env.js+3 1 modified
    @@ -14,7 +14,9 @@
             'compress',         // option - whether to compress
             'processImports',   // option - whether to process imports. if false then imports will not be imported
             'syncImport',       // option - whether to import synchronously
    -        'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true
    +        /* BEGIN MODIFICATION */
    +        // Removed 'javascriptEnabled'
    +        /* END MODIFICATION */
             'mime',             // browser only - mime type for sheet import
             'useFileCache',     // browser only - whether to use the per file session cache
             'currentFileInfo'   // information about the current file - for error reporting and importing and making urls relative etc.
    
  • lib/thirdparty/less/functions.js+5 1 modified
    @@ -213,7 +213,11 @@ tree.functions = {
             }
         },
         e: function (str) {
    -        return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str);
    +        /* BEGIN MODIFICATION */
    +        // Removed handling of tree.JavaScript
    +        return new(tree.Anonymous)(str);
    +        /* END MODIFICATION */
    +
         },
         escape: function (str) {
             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+3 1 modified
    @@ -112,7 +112,9 @@ require('./tree/mixin');
     require('./tree/comment');
     require('./tree/anonymous');
     require('./tree/value');
    -require('./tree/javascript');
    +/* BEGIN MODIFICATION */
    +// Removed require('./tree/javascript');
    +/* END MODIFICATION */
     require('./tree/assignment');
     require('./tree/condition');
     require('./tree/paren');
    
  • lib/thirdparty/less/lessc_helper.js+3 1 modified
    @@ -31,7 +31,9 @@ var lessc_helper = {
             console.log("  -M, --depends            Output a makefile import dependency list to stdout");
             console.log("  --no-color               Disable colorized output.");
             console.log("  --no-ie-compat           Disable IE compatibility checks.");
    -        console.log("  --no-js                  Disable JavaScript in less files");
    +        /* BEGIN MODIFICATION */
    +        // Removed --no-js option
    +        /* END MODIFICATION */
             console.log("  -l, --lint               Syntax check only (lint).");
             console.log("  -s, --silent             Suppress output of error messages.");
             console.log("  --strict-imports         Force evaluation of imports.");
    
  • lib/thirdparty/less/parser.js+7 11 modified
    @@ -993,27 +993,23 @@ less.Parser = function Parser(env) {
                         }
                     },
     
    +                /* BEGIN MODIFICATION */
    +                // Removed support for javascript
    +
                     //
    -                // JavaScript code to be evaluated
    +                // JavaScript code (disabled)
                     //
                     //     `window.location.href`
                     //
                     javascript: function () {
    -                    var str, j = i, e;
    +                    var j = i, e;
     
                         if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings
                         if (input.charAt(j) !== '`') { return; }
    -                    if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) {
    -                        error("You are using JavaScript, which has been disabled.");
    -                    }
    -
    -                    if (e) { $char('~'); }
     
    -                    str = $re(/^`([^`]*)`/);
    -                    if (str) {
    -                        return new(tree.JavaScript)(str[1], i, e);
    -                    }
    +                    error("You are using JavaScript, which has been disabled.");
                     }
    +                /* END MODIFICATION */
                 },
     
                 //
    
  • lib/thirdparty/less/README.md+4 0 modified
    @@ -3,3 +3,7 @@
     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.
     
     The files `browser.js` and `rhino.js` have been removed, as they are not relevant for the Node.js implementation.
    +
    +The file `tree/javascript.js` has been removed to disable JavaScript execution.
    +
    +Modifications within the files are marked with `/* BEGIN MODIFICATION */` and `/* END MODIFICATION */` comments.
    
  • lib/thirdparty/less/tree/javascript.js+0 58 removed
    @@ -1,58 +0,0 @@
    -(function (tree) {
    -
    -tree.JavaScript = function (string, index, escaped) {
    -    this.escaped = escaped;
    -    this.expression = string;
    -    this.index = index;
    -};
    -tree.JavaScript.prototype = {
    -    type: "JavaScript",
    -    eval: function (env) {
    -        var result,
    -            that = this,
    -            context = {};
    -
    -        var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) {
    -            return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env));
    -        });
    -
    -        try {
    -            expression = new(Function)('return (' + expression + ')');
    -        } catch (e) {
    -            throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" ,
    -                    index: this.index };
    -        }
    -
    -        var variables = env.frames[0].variables();
    -        for (var k in variables) {
    -            if (variables.hasOwnProperty(k)) {
    -                /*jshint loopfunc:true */
    -                context[k.slice(1)] = {
    -                    value: variables[k].value,
    -                    toJS: function () {
    -                        return this.value.eval(env).toCSS();
    -                    }
    -                };
    -            }
    -        }
    -
    -        try {
    -            result = expression.call(context);
    -        } catch (e) {
    -            throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" ,
    -                    index: this.index };
    -        }
    -        if (typeof(result) === 'number') {
    -            return new(tree.Dimension)(result);
    -        } else if (typeof(result) === 'string') {
    -            return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index);
    -        } else if (Array.isArray(result)) {
    -            return new(tree.Anonymous)(result.join(', '));
    -        } else {
    -            return new(tree.Anonymous)(result);
    -        }
    -    }
    -};
    -
    -})(require('../tree'));
    -
    
  • lib/thirdparty/less/tree/quoted.js+7 1 modified
    @@ -22,7 +22,13 @@ tree.Quoted.prototype = {
         eval: function (env) {
             var that = this;
             var value = this.value.replace(/`([^`]+)`/g, function (_, exp) {
    -            return new(tree.JavaScript)(exp, that.index, true).eval(env).value;
    +            /* BEGIN MODIFICATION */
    +            // Removed support for javascript
    +            const error = new Error("You are using JavaScript, which has been disabled.");
    +            error.index = that.index;
    +            error.type = "Syntax";
    +            throw error;
    +            /* END MODIFICATION */
             }).replace(/@\{([\w-]+)\}/g, function (_, name) {
                 var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true);
                 return (v instanceof tree.Quoted) ? v.value : v.toCSS();
    
  • README.md+4 1 modified
    @@ -149,7 +149,10 @@ lib2
     Type: `object`
     
     Options for the [less](http://lesscss.org) parser (`less.Parser`).  
    -**Note:** Default of `relativeUrls` option is changed from `false` to `true`.
    +
    +**Note**
    +- Default of `relativeUrls` option is changed from `false` to `true`.
    +- Option `javascriptEnabled` has been removed. JavaScript is always disabled and cannot be enabled.
     
     ##### compiler
     
    
  • test/test.js+108 0 modified
    @@ -419,6 +419,114 @@ describe("error handling", function() {
     			assert.ok(err);
     		});
     	});
    +
    +	it("should throw error when using inline JavaScript", function() {
    +		const lessInput = `.rule {
    +	@var: \`(function(){ return "Cat"; })()\`;
    +	color: @var;
    +}`;
    +		return new Builder().build({
    +			lessInput
    +		}).then(function(res) {
    +			// no resolve
    +			assert.ok(false, `Expected build to fail but finished with content:\n${res.css}`);
    +		}, function(err) {
    +			assert.equal(err.message, "You are using JavaScript, which has been disabled.");
    +			assert.ok(err);
    +		});
    +	});
    +
    +	it("should throw error when using quoted inline JavaScript", function() {
    +		const lessInput = `.rule {
    +	@var: "\`(function(){ return 'Cat'; })()\`";
    +	color: @var;
    +}`;
    +		return new Builder().build({
    +			lessInput
    +		}).then(function(res) {
    +			// no resolve
    +			assert.ok(false, `Expected build to fail but finished with content:\n${res.css}`);
    +		}, function(err) {
    +			assert.equal(err.message, "You are using JavaScript, which has been disabled.");
    +			assert.ok(err);
    +		});
    +	});
    +
    +	it("should throw error when using inline JavaScript with parser option javascriptEnabled: true", function() {
    +		const lessInput = `.rule {
    +	@var: \`(function(){ return "Cat"; })()\`;
    +	color: @var;
    +}`;
    +		return new Builder().build({
    +			lessInput,
    +			parser: {
    +				javascriptEnabled: true
    +			}
    +		}).then(function(res) {
    +			// no resolve
    +			assert.ok(false, `Expected build to fail but finished with content:\n${res.css}`);
    +		}, function(err) {
    +			assert.equal(err.message, "You are using JavaScript, which has been disabled.");
    +			assert.ok(err);
    +		});
    +	});
    +
    +	it("should throw error when using quoted inline JavaScript with parser option javascriptEnabled: true", function() {
    +		const lessInput = `.rule {
    +	@var: "\`(function(){ return 'Cat'; })()\`";
    +	color: @var;
    +}`;
    +		return new Builder().build({
    +			lessInput,
    +			parser: {
    +				javascriptEnabled: true
    +			}
    +		}).then(function(res) {
    +			// no resolve
    +			assert.ok(false, `Expected build to fail but finished with content:\n${res.css}`);
    +		}, function(err) {
    +			assert.equal(err.message, "You are using JavaScript, which has been disabled.");
    +			assert.ok(err);
    +		});
    +	});
    +
    +	it("should throw error when using inline JavaScript with parser option javascriptEnabled: false", function() {
    +		const lessInput = `.rule {
    +	@var: \`(function(){ return "Cat"; })()\`;
    +	color: @var;
    +}`;
    +		return new Builder().build({
    +			lessInput,
    +			parser: {
    +				javascriptEnabled: false
    +			}
    +		}).then(function(res) {
    +			// no resolve
    +			assert.ok(false, `Expected build to fail but finished with content:\n${res.css}`);
    +		}, function(err) {
    +			assert.equal(err.message, "You are using JavaScript, which has been disabled.");
    +			assert.ok(err);
    +		});
    +	});
    +
    +	it("should throw error when using quoted inline JavaScript with parser option javascriptEnabled: false", function() {
    +		const lessInput = `.rule {
    +	@var: "\`(function(){ return 'Cat'; })()\`";
    +	color: @var;
    +}`;
    +		return new Builder().build({
    +			lessInput,
    +			parser: {
    +				javascriptEnabled: false
    +			}
    +		}).then(function(res) {
    +			// no resolve
    +			assert.ok(false, `Expected build to fail but finished with content:\n${res.css}`);
    +		}, function(err) {
    +			assert.equal(err.message, "You are using JavaScript, which has been disabled.");
    +			assert.ok(err);
    +		});
    +	});
     });
     
     function assertLessToRtlCssEqual(filename) {
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

7

News mentions

0

No linked articles in our index yet.