Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
SpEL2JS is a plugin that will parse Spring Expression Language within a defined context in JavaScript. This is useful in single-page applications where duplication of authorization expressions for UI purposes can lead to inconsistencies. This library implements a JavaScript version of the parser based on the documentation in the link above. I did my best to followed the docs as closely as possible, but if you come accross an expression that behaves differently than you would expect then please open an issue.
Install SpEL2JS:
$ npm i -S spel2js
# or
$ bower i -S spel2js
Include the dependency using a module loader or script tag.
SpEL2JS exports a singleton with two members:
import spel2js from 'spel2js';
console.log(spel2js);
/*
{
StandardContext,
SpelExpressionEvaluator
}
*/
StandardContext
The StandardContext
is a factory that creates a evaluation context for an expression.
NOTE: This is not the same as the Java EvaluationContext
class, though it serves a similar purpose.
let spelContext = spel2js.StandardContext.create(authentication, principal);
The create()
method takes two arguments: authentication
and principal
authentication
is an instance of Spring's Authentication
class from Spring Security.
principal
is any object representing the user (this is just used for reference, and can be any value or structure)
SpelExpressionEvaluator
The heavy lifting is done using the SpelExpressionEvaluator
which exposes two functions: compile()
and eval()
compile()
pre-compiles a SpEL expression, and returns an object with an eval()
method that takes a context and optional locals:
import { StandardContext, SpelExpressionEvaluator } from 'spel2js';
const expression = '#toDoList.owner == authentication.details.name';
const spelContext = StandardContext.create(authentication, principal);
const locals = {
toDoList: {
owner: 'Darth Vader'
}
};
const compiledExpression = SpelExpressionEvaluator.compile(expression);
compiledExpression.eval(spelContext, locals); // true
eval()
is just a shortcut for immediately evaluating an expression instead of pre-compiling:
import { StandardContext, SpelExpressionEvaluator } from 'spel2js';
const expression = '#toDoList.owner == authentication.details.name';
const spelContext = StandardContext.create(authentication, principal);
const locals = {
toDoList: {
owner: 'Darth Vader'
}
};
SpelExpressionEvaluator.eval(expression, spelContext, locals); // true
Create a single context that contains information about the current user and reuse it for all evaluations. This way, you only have to supply an expression and locals when evaluating.
Always pre-compile your expressions! Compilation takes much longer than evaluation; doing it up-front saves CPU when evaluating later.
Say you are creating a shared to-do list, and you want to allow only the owner of the list to make changes, but anyone can view:
//ListController.java
@Controller
@RequestMapping('/todolists')
public class ListController {
public static final String ADD_LIST_ITEM_PERMISSION = "#toDoList.owner == authentication.details.name";
...
@PreAuthorize(ADD_LIST_ITEM_PERMISSION)
@RequestMapping(value="/{toDolistId}/items", method=RequestMethod.POST)
public ResponseEntity<ListItem> addListItem(@MagicAnnotation ToDoList toDoList, @RequestBody ListItem newListItem) {
//add the item to the list
return new ResponseEntity<ListItem>(newListItem, HttpStatus.CREATED);
}
...
}
//spel-service.js
import { StandardContext, SpelExpressionEvaluator } from 'spel2js';
// wraps spel2js in a stateful service that simplifies evaluation
angular.module('ToDo').factory('SpelService', function () {
return {
context: null,
// assume this is called on page load
setContext(authentication, principal) {
this.context = StandardContext.create(authentication, principal);
},
getContext() { return this.context; },
compile(expression) {
const compiledExpression = SpelExpressionEvaluator.compile(expression);
return {
eval(locals) {
return compiledExpression.eval(this.getContext(), locals);
}
};
},
eval(expression, locals) {
return SpelExpressionEvaluator.eval(expression, this.getContext(), locals);
}
};
});
//list-controller.js
angular.module('ToDo').controller('ListController', ['$http', '$scope', 'SpelService', function ($http, $scope, SpelService) {
// retrieve all permissions and pre-compile them
$http.get('/api/permissions').success(function (permissions) {
angular.forEach(permissions, function (spelExpression, key) {
$scope.permissions[key] = SpelService.compile(spelExpression);
});
});
// $scope will be used as locals
$scope.list = {
name: 'My List',
owner: 'Ben March',
items: [
{
text: 'List item number 1!'
}
]
}
// EXPAMPLE 1: authorize a request before making it
$scope.addListItem = function (list, newListItem) {
if ($scope.permissions.ADD_LIST_ITEM_PERMISSION.eval($scope)) {
$http.post('/todolists/' + list.id + '/items', item).success(function () {...});
}
}
}]);
<!--list-controller.html-->
<div ng-controller="ListController">
...
<li ng-repeat="listItem in list.items">
<p>{{listItem.text}}</p>
</li>
<li class="list-actions">
<input type="text" ng-model="newListItem.text" />
<!-- EXAMPLE 2: Hide the button if the user does not have permission -->
<button ng-click="addListItem(list, newListItem)" ng-if="permissions.ADD_LIST_ITEM_PERMISSION.eval(this)">Add</button>
</li>
...
</div>
Now the UI can always stay in sync with the server-side authorities.
This is now in a stable state and will be released as 0.2.0. The following features are tested and working:
The following are not implemented yet because I'm not sure of the best approach:
If someone wants to implement a REST-compliant way in Spring to expose the permissions (and maybe the custom PermissionEvaluators) that would be awesome.
$ npm i
$ npm run build
$ npm test
Credit is given to all of the original authors of the Java SpEL implementation at the time of this library's creation:
This repository was scaffolded with generator-microjs.
Since this was ported from the Spring Framework, this library is under version 2.0 of the Apache License.
FAQs
Parse Spring Expression Language in JavaScript
The npm package spel2js receives a total of 49,762 weekly downloads. As such, spel2js popularity was classified as popular.
We found that spel2js demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.