Skip to content

Commit

Permalink
feat: default options for controlling proto access
Browse files Browse the repository at this point in the history
This commmit adds the runtime options
- `allowProtoPropertiesByDefault` (boolean) and
- `allowProtoMethodsByDefault` (boolean)`
which can be used to allow access to prototype properties and
functions in general.

Specific properties and methods can still be disabled from access
via `allowedProtoProperties` and `allowedProtoMethods` by
setting the corresponding values to false.

The methods 'constructor', '__defineGetter__', '__defineSetter__', '__lookupGetter__'
and the property '__proto__' will be disabled, even if the allow...ByDefault-options
are set to true. In order to allow access to those properties and methods, they have
to be explicitly set to true in the 'allowedProto...'-options.
  • Loading branch information
nknapp committed Jan 9, 2020
1 parent 91a1b5d commit 6ed0598
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 121 deletions.
48 changes: 48 additions & 0 deletions lib/handlebars/internal/proto-access.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { createNewLookupObject } from './create-new-lookup-object';

export function createProtoAccessControl(runtimeOptions) {
let defaultMethodWhiteList = Object.create(null);
defaultMethodWhiteList['constructor'] = false;
defaultMethodWhiteList['__defineGetter__'] = false;
defaultMethodWhiteList['__defineSetter__'] = false;
defaultMethodWhiteList['__lookupGetter__'] = false;

let defaultPropertyWhiteList = Object.create(null);
// eslint-disable-next-line no-proto
defaultPropertyWhiteList['__proto__'] = false;

return {
properties: {
whitelist: createNewLookupObject(
defaultPropertyWhiteList,
runtimeOptions.allowedProtoProperties
),
defaultValue: runtimeOptions.allowProtoPropertiesByDefault
},
methods: {
whitelist: createNewLookupObject(
defaultMethodWhiteList,
runtimeOptions.allowedProtoMethods
),
defaultValue: runtimeOptions.allowProtoMethodsByDefault
}
};
}

export function resultIsAllowed(result, protoAccessControl, propertyName) {
if (typeof result === 'function') {
return checkWhiteList(protoAccessControl.methods, propertyName);
} else {
return checkWhiteList(protoAccessControl.properties, propertyName);
}
}

function checkWhiteList(protoAccessControlForType, propertyName) {
if (protoAccessControlForType.whitelist[propertyName] !== undefined) {
return protoAccessControlForType.whitelist[propertyName] === true;
}
if (protoAccessControlForType.defaultValue !== undefined) {
return protoAccessControlForType.defaultValue;
}
return false;
}
26 changes: 10 additions & 16 deletions lib/handlebars/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import {
} from './base';
import { moveHelperToHooks } from './helpers';
import { wrapHelper } from './internal/wrapHelper';
import { createNewLookupObject } from './internal/createNewLookupObject';
import {
createProtoAccessControl,
resultIsAllowed
} from './internal/proto-access';

export function checkRevision(compilerInfo) {
const compilerRevision = (compilerInfo && compilerInfo[0]) || 1,
Expand Down Expand Up @@ -73,8 +76,7 @@ export function template(templateSpec, env) {

let extendedOptions = Utils.extend({}, options, {
hooks: this.hooks,
allowedProtoMethods: this.allowedProtoMethods,
allowedProtoProperties: this.allowedProtoProperties
protoAccessControl: this.protoAccessControl
});

let result = env.VM.invokePartial.call(
Expand Down Expand Up @@ -129,12 +131,8 @@ export function template(templateSpec, env) {
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
return result;
}
const whitelist =
typeof result === 'function'
? container.allowedProtoMethods
: container.allowedProtoProperties;

if (whitelist[propertyName] === true) {
if (resultIsAllowed(result, container.protoAccessControl, propertyName)) {
return result;
}
return undefined;
Expand Down Expand Up @@ -237,6 +235,7 @@ export function template(templateSpec, env) {
)
);
}

main = executeDecorators(
templateSpec.main,
main,
Expand All @@ -247,6 +246,7 @@ export function template(templateSpec, env) {
);
return main(context, options);
}

ret.isTop = true;

ret._setup = function(options) {
Expand All @@ -271,21 +271,15 @@ export function template(templateSpec, env) {
}

container.hooks = {};
container.allowedProtoProperties = createNewLookupObject(
options.allowedProtoProperties
);
container.allowedProtoMethods = createNewLookupObject(
options.allowedProtoMethods
);
container.protoAccessControl = createProtoAccessControl(options);

let keepHelperInHelpers =
options.allowCallsToHelperMissing ||
templateWasPrecompiledWithCompilerV7;
moveHelperToHooks(container, 'helperMissing', keepHelperInHelpers);
moveHelperToHooks(container, 'blockHelperMissing', keepHelperInHelpers);
} else {
container.allowedProtoProperties = options.allowedProtoProperties;
container.allowedProtoMethods = options.allowedProtoMethods;
container.protoAccessControl = options.protoAccessControl; // internal option
container.helpers = options.helpers;
container.partials = options.partials;
container.decorators = options.decorators;
Expand Down
Loading

0 comments on commit 6ed0598

Please sign in to comment.