Fix for #4308 - triple brace conversion was not working correctly, wrote this into the string templates instead - also fixing an issue with the RBAC for Rest.

This commit is contained in:
mike12345567 2022-02-03 18:26:26 +00:00
parent b40117464e
commit 61283e465d
7 changed files with 66 additions and 14 deletions

View File

@ -26,6 +26,7 @@
async function getPermissions(queryToFetch) { async function getPermissions(queryToFetch) {
if (fetched?._id === queryToFetch?._id) { if (fetched?._id === queryToFetch?._id) {
loaded = true
return return
} }
fetched = queryToFetch fetched = queryToFetch

View File

@ -5,9 +5,6 @@ const { integrations } = require("../integrations")
const { processStringSync } = require("@budibase/string-templates") const { processStringSync } = require("@budibase/string-templates")
const { doInAppContext, getAppDB } = require("@budibase/backend-core/context") const { doInAppContext, getAppDB } = require("@budibase/backend-core/context")
const IS_TRIPLE_BRACE = new RegExp(/^{{3}.*}{3}$/)
const IS_HANDLEBARS = new RegExp(/^{{2}.*}{2}$/)
class QueryRunner { class QueryRunner {
constructor(input, flags = { noRecursiveQuery: false }) { constructor(input, flags = { noRecursiveQuery: false }) {
this.datasource = input.datasource this.datasource = input.datasource
@ -188,12 +185,8 @@ class QueryRunner {
enrichedQuery[key] = this.enrichQueryFields(fields[key], parameters) enrichedQuery[key] = this.enrichQueryFields(fields[key], parameters)
} else if (typeof fields[key] === "string") { } else if (typeof fields[key] === "string") {
// enrich string value as normal // enrich string value as normal
let value = fields[key] enrichedQuery[key] = processStringSync(fields[key], parameters, {
// add triple brace to avoid escaping e.g. '=' in cookie header noEscaping: true,
if (IS_HANDLEBARS.test(value) && !IS_TRIPLE_BRACE.test(value)) {
value = `{${value}}`
}
enrichedQuery[key] = processStringSync(value, parameters, {
noHelpers: true, noHelpers: true,
}) })
} else { } else {

View File

@ -17,6 +17,7 @@ module.exports.processString = templates.processString
module.exports.processObject = templates.processObject module.exports.processObject = templates.processObject
module.exports.doesContainStrings = templates.doesContainStrings module.exports.doesContainStrings = templates.doesContainStrings
module.exports.doesContainString = templates.doesContainString module.exports.doesContainString = templates.doesContainString
module.exports.disableEscaping = templates.disableEscaping
/** /**
* Use vm2 to run JS scripts in a node env * Use vm2 to run JS scripts in a node env
@ -27,4 +28,4 @@ setJSRunner((js, context) => {
timeout: 1000 timeout: 1000
}) })
return vm.run(js) return vm.run(js)
}) })

View File

@ -3,12 +3,12 @@ const { registerAll } = require("./helpers/index")
const processors = require("./processors") const processors = require("./processors")
const { atob, btoa } = require("./utilities") const { atob, btoa } = require("./utilities")
const manifest = require("../manifest.json") const manifest = require("../manifest.json")
const { FIND_HBS_REGEX } = require("./utilities") const { FIND_HBS_REGEX, FIND_DOUBLE_HBS_REGEX } = require("./utilities")
const hbsInstance = handlebars.create() const hbsInstance = handlebars.create()
registerAll(hbsInstance) registerAll(hbsInstance)
const hbsInstanceNoHelpers = handlebars.create() const hbsInstanceNoHelpers = handlebars.create()
const defaultOpts = { noHelpers: false } const defaultOpts = { noHelpers: false, noEscaping: false }
/** /**
* utility function to check if the object is valid * utility function to check if the object is valid
@ -58,7 +58,11 @@ module.exports.processObject = async (object, context, opts) => {
* then nothing will occur. * then nothing will occur.
* @param {string} string The template string which is the filled from the context object. * @param {string} string The template string which is the filled from the context object.
* @param {object} context An object of information which will be used to enrich the string. * @param {object} context An object of information which will be used to enrich the string.
<<<<<<< HEAD
* @param {object|null} opts optional - specify some options for processing. * @param {object|null} opts optional - specify some options for processing.
=======
* @param {object|undefined} opts optional - specify some options for processing.
>>>>>>> e12767fd8... Fix for #4308 - triple brace conversion was not working correctly, wrote this into the string templates instead - also fixing an issue with the RBAC for Rest.
* @returns {Promise<string>} The enriched string, all templates should have been replaced if they can be. * @returns {Promise<string>} The enriched string, all templates should have been replaced if they can be.
*/ */
module.exports.processString = async (string, context, opts) => { module.exports.processString = async (string, context, opts) => {
@ -72,7 +76,11 @@ module.exports.processString = async (string, context, opts) => {
* @param {object|array} object The input structure which is to be recursed, it is important to note that * @param {object|array} object The input structure which is to be recursed, it is important to note that
* if the structure contains any cycles then this will fail. * if the structure contains any cycles then this will fail.
* @param {object} context The context that handlebars should fill data from. * @param {object} context The context that handlebars should fill data from.
<<<<<<< HEAD
* @param {object|null} opts optional - specify some options for processing. * @param {object|null} opts optional - specify some options for processing.
=======
* @param {object|undefined} opts optional - specify some options for processing.
>>>>>>> e12767fd8... Fix for #4308 - triple brace conversion was not working correctly, wrote this into the string templates instead - also fixing an issue with the RBAC for Rest.
* @returns {object|array} The structure input, as fully updated as possible. * @returns {object|array} The structure input, as fully updated as possible.
*/ */
module.exports.processObjectSync = (object, context, opts) => { module.exports.processObjectSync = (object, context, opts) => {
@ -93,7 +101,11 @@ module.exports.processObjectSync = (object, context, opts) => {
* then nothing will occur. This is a pure sync call and therefore does not have the full functionality of the async call. * then nothing will occur. This is a pure sync call and therefore does not have the full functionality of the async call.
* @param {string} string The template string which is the filled from the context object. * @param {string} string The template string which is the filled from the context object.
* @param {object} context An object of information which will be used to enrich the string. * @param {object} context An object of information which will be used to enrich the string.
<<<<<<< HEAD
* @param {object|null} opts optional - specify some options for processing. * @param {object|null} opts optional - specify some options for processing.
=======
* @param {object|undefined} opts optional - specify some options for processing.
>>>>>>> e12767fd8... Fix for #4308 - triple brace conversion was not working correctly, wrote this into the string templates instead - also fixing an issue with the RBAC for Rest.
* @returns {string} The enriched string, all templates should have been replaced if they can be. * @returns {string} The enriched string, all templates should have been replaced if they can be.
*/ */
module.exports.processStringSync = (string, context, opts) => { module.exports.processStringSync = (string, context, opts) => {
@ -110,7 +122,9 @@ module.exports.processStringSync = (string, context, opts) => {
string = processors.preprocess(string, shouldFinalise) string = processors.preprocess(string, shouldFinalise)
// this does not throw an error when template can't be fulfilled, have to try correct beforehand // this does not throw an error when template can't be fulfilled, have to try correct beforehand
const instance = opts.noHelpers ? hbsInstanceNoHelpers : hbsInstance const instance = opts.noHelpers ? hbsInstanceNoHelpers : hbsInstance
const template = instance.compile(string, { const templateString =
opts && opts.noEscaping ? exports.disableEscaping(string) : string
const template = instance.compile(templateString, {
strict: false, strict: false,
}) })
const now = Math.floor(Date.now() / 1000) * 1000 const now = Math.floor(Date.now() / 1000) * 1000
@ -125,6 +139,24 @@ module.exports.processStringSync = (string, context, opts) => {
} }
} }
/**
* By default with expressions like {{ name }} handlebars will escape various
* characters, which can be problematic. To fix this we use the syntax {{{ name }}},
* this function will find any double braces and switch to triple.
* @param string the string to have double HBS statements converted to triple.
*/
module.exports.disableEscaping = string => {
let regexp = new RegExp(FIND_DOUBLE_HBS_REGEX)
const matches = string.match(regexp)
if (matches == null) {
return string
}
for (let match of matches) {
string = string.replace(match, `{${match}}`)
}
return string
}
/** /**
* Simple utility function which makes sure that a templating property has been wrapped in literal specifiers correctly. * Simple utility function which makes sure that a templating property has been wrapped in literal specifiers correctly.
* @param {string} property The property which is to be wrapped. * @param {string} property The property which is to be wrapped.

View File

@ -17,6 +17,7 @@ export const processString = templates.processString
export const processObject = templates.processObject export const processObject = templates.processObject
export const doesContainStrings = templates.doesContainStrings export const doesContainStrings = templates.doesContainStrings
export const doesContainString = templates.doesContainString export const doesContainString = templates.doesContainString
export const disableEscaping = templates.disableEscaping
/** /**
* Use polyfilled vm to run JS scripts in a browser Env * Use polyfilled vm to run JS scripts in a browser Env
@ -30,4 +31,4 @@ setJSRunner((js, context) => {
} }
vm.createContext(context) vm.createContext(context)
return vm.runInNewContext(js, context, { timeout: 1000 }) return vm.runInNewContext(js, context, { timeout: 1000 })
}) })

View File

@ -1,6 +1,7 @@
const ALPHA_NUMERIC_REGEX = /^[A-Za-z0-9]+$/g const ALPHA_NUMERIC_REGEX = /^[A-Za-z0-9]+$/g
module.exports.FIND_HBS_REGEX = /{{([^{].*?)}}/g module.exports.FIND_HBS_REGEX = /{{([^{].*?)}}/g
module.exports.FIND_DOUBLE_HBS_REGEX = /(?<!{){{[^{}]+}}(?!})/g
module.exports.isAlphaNumeric = char => { module.exports.isAlphaNumeric = char => {
return char.match(ALPHA_NUMERIC_REGEX) return char.match(ALPHA_NUMERIC_REGEX)

View File

@ -4,8 +4,12 @@ const {
isValid, isValid,
makePropSafe, makePropSafe,
getManifest, getManifest,
<<<<<<< HEAD
encodeJSBinding, encodeJSBinding,
doesContainString, doesContainString,
=======
disableEscaping,
>>>>>>> e12767fd8... Fix for #4308 - triple brace conversion was not working correctly, wrote this into the string templates instead - also fixing an issue with the RBAC for Rest.
} = require("../src/index.cjs") } = require("../src/index.cjs")
describe("Test that the string processing works correctly", () => { describe("Test that the string processing works correctly", () => {
@ -176,3 +180,22 @@ describe("check does contain string function", () => {
expect(doesContainString(js, "foo")).toEqual(true) expect(doesContainString(js, "foo")).toEqual(true)
}) })
}) })
describe("check that disabling escaping function works", () => {
it("should work for a single statement", () => {
expect(disableEscaping("{{ name }}")).toEqual("{{{ name }}}")
})
it("should work for two statements", () => {
expect(disableEscaping("{{ name }} welcome to {{ platform }}")).toEqual("{{{ name }}} welcome to {{{ platform }}}")
})
it("shouldn't convert triple braces", () => {
expect(disableEscaping("{{{ name }}}")).toEqual("{{{ name }}}")
})
it("should work with a combination", () => {
expect(disableEscaping("{{ name }} welcome to {{{ platform }}}")).toEqual("{{{ name }}} welcome to {{{ platform }}}")
})
})