Skip to content

Commit

Permalink
feat: STRF-11673 Add nonce helper (#299)
Browse files Browse the repository at this point in the history
  • Loading branch information
jairo-bc authored Mar 12, 2024
1 parent 276d6b1 commit 2e329af
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 9 deletions.
1 change: 1 addition & 0 deletions helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const helpersList = [
'truncate',
'unless',
'earlyHint',
'nonce',
];

const deprecatedHelpersList = [
Expand Down
16 changes: 16 additions & 0 deletions helpers/nonce.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use strict';

const factory = globals => {
return function() {
const params = globals.getRequestParams();
if (params && params.security && params.security.nonce) {
return ` nonce="${params.security.nonce}"`;
}
return ''
};
};

module.exports = [{
name: 'nonce',
factory: factory,
}];
23 changes: 22 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class HandlebarsRenderer {
* @param {Object} logger - A console-like object to use for logging
* @param {String} logLevel - log level which will be overriden by renderer
*/
constructor(siteSettings, themeSettings, hbVersion, logger = console, logLevel = 'info') {
constructor(siteSettings, themeSettings, hbVersion, logger = console, logLevel = 'info', params = {}) {
// Figure out which version of Handlebars to use.
switch(hbVersion) {
case 'v4':
Expand All @@ -56,12 +56,14 @@ class HandlebarsRenderer {
this.setContent({});
this.resetDecorators();
this.setLoggerLevel(logLevel);
this.setRequestParams(params);

// Build global context for helpers
this.helperContext = {
handlebars: this.handlebars,
getSiteSettings: this.getSiteSettings.bind(this),
getThemeSettings: this.getThemeSettings.bind(this),
getRequestParams: this.getRequestParams.bind(this),
getTranslator: this.getTranslator.bind(this),
getContent: this.getContent.bind(this),
getLogger: this.getLogger.bind(this),
Expand Down Expand Up @@ -134,6 +136,25 @@ class HandlebarsRenderer {
return this._themeSettings;
};


/**
* Set the request params object containing the request parameters.
*
* @param {object} params
*/
setRequestParams(params) {
this._params = params;
}

/**
* Get the request params object containing the request parameters.
*
* @returns {object} params
*/
getRequestParams() {
return this._params;
}

/**
* Reset decorator list.
*/
Expand Down
1 change: 1 addition & 0 deletions spec/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ describe('helper registration', () => {
'getShortMonth',
'pick',
'earlyHint',
'nonce'
].sort();

expect(helpers.map(helper => helper.name).sort()).to.be.equal(expectedHelpers);
Expand Down
38 changes: 38 additions & 0 deletions spec/helpers/nonce.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const Lab = require('lab');
const lab = exports.lab = Lab.script();
const { describe, it} = lab;
const {testRunner, buildRenderer, randomString} = require('../spec-helpers');

describe('nonce helper', function () {
const context = {}

it('should render a nonce html attribute with the correct value from request params', function (done) {
const requestParams = {
security: {
nonce: randomString()
},
}
const renderer = buildRenderer({}, {}, {}, null, requestParams);
const runTestCases = testRunner({context, renderer});
runTestCases([
{
input: '{{nonce}}',
output: ' nonce="' + requestParams.security.nonce + '"',
},
], done);
});

it('should not render a nonce since request param is empty', function (done) {
const requestParams = {
security: {},
}
const renderer = buildRenderer({}, {}, {}, null, requestParams);
const runTestCases = testRunner({context, renderer});
runTestCases([
{
input: '{{nonce}}',
output: '',
},
], done);
});
});
18 changes: 10 additions & 8 deletions spec/spec-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ const Crypto = require('crypto');
const Renderer = require('../index');
const expect = require('code').expect;

function buildRenderer(siteSettings, themeSettings, templates, hbVersion) {
function buildRenderer(siteSettings, themeSettings, templates, hbVersion, requestParams) {
siteSettings = siteSettings || {};
themeSettings = themeSettings || {};
hbVersion = hbVersion || 'v3';
const renderer = new Renderer(siteSettings, themeSettings, hbVersion);
requestParams = requestParams || {};
const renderer = new Renderer(siteSettings, themeSettings, hbVersion, console, 'info', requestParams);

// Register templates
if (typeof templates !== 'undefined') {
Expand All @@ -17,26 +18,27 @@ function buildRenderer(siteSettings, themeSettings, templates, hbVersion) {
return renderer;
}

function renderString(template, context, siteSettings, themeSettings, templates) {
const renderer = buildRenderer(siteSettings, themeSettings, templates);
function renderString(template, context, siteSettings, themeSettings, templates, hbVersion, requestParams) {
const renderer = buildRenderer(siteSettings, themeSettings, templates, hbVersion, requestParams);
return renderer.renderString(template, context);
}

function render(template, context, siteSettings, themeSettings, templates) {
const renderer = buildRenderer(siteSettings, themeSettings, templates);
function render(template, context, siteSettings, themeSettings, templates, hbVersion, requestParams) {
const renderer = buildRenderer(siteSettings, themeSettings, templates, hbVersion, requestParams);
return renderer.render(template, context);
}

// Return a function that is used to run through a set of test cases using renderString
function testRunner({context, siteSettings, themeSettings, templates, renderer, hbVersion}) {
function testRunner({context, siteSettings, themeSettings, templates, renderer, hbVersion, requestParams}) {
return (testCases, done) => {
const promises = testCases.map(testCase => {
const render = testCase.renderer ||
renderer ||
buildRenderer(testCase.siteSettings || siteSettings,
testCase.themeSettings || themeSettings,
testCase.templates || templates,
testCase.hbVersion || hbVersion);
testCase.hbVersion || hbVersion,
testCase.requestParams || requestParams);

return render.renderString(testCase.input, testCase.context || context).then(result => {
expect(result).to.be.equal(testCase.output);
Expand Down

0 comments on commit 2e329af

Please sign in to comment.