From 88865ab054388257ae5be9ad68ef7d6cc671ffd1 Mon Sep 17 00:00:00 2001 From: Joel Date: Wed, 26 Apr 2017 14:32:46 -0700 Subject: [PATCH] Issue #113: Escape backticks in generated sync functions To avoid syntax errors when a generated sync function is inserted as a multiline string in a Sync Gateway configuration file, all backtick characters that appear in the sync function are now automatically escaped. --- CHANGELOG.md | 3 +++ etc/sync-function-loader.js | 6 ++++-- etc/test-helper.js | 16 ++++++++++++++-- package.json | 2 +- test/hashtable-spec.js | 4 ++-- test/resources/hashtable-doc-definitions.js | 2 +- test/resources/string-doc-definitions.js | 2 +- test/string-spec.js | 7 +++++-- 8 files changed, 31 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a6d283d..321335b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). - [#97](https://github.com/Kashoo/synctos/issues/97): Support dynamic document constraints - [#100](https://github.com/Kashoo/synctos/issues/100): Option to initialize test helper module with document definition file +### Fixed +- [#113](https://github.com/Kashoo/synctos/issues/113): Backticks in document definitions cause syntax errors + ## [1.8.0] - 2017-03-21 ### Added - [#90](https://github.com/Kashoo/synctos/issues/90): Document-wide constraints on file attachments diff --git a/etc/sync-function-loader.js b/etc/sync-function-loader.js index 0936776b..bda143b3 100644 --- a/etc/sync-function-loader.js +++ b/etc/sync-function-loader.js @@ -44,8 +44,10 @@ exports.load = function(docDefinitionFilename, baseDir) { syncDocDefn = syncDocDefn.replace(/importDocumentDefinitionFragment\s*\(\s*"((?:\\"|[^"])+)"\s*\)/g, readDocDefinitionFragment) .replace(/importDocumentDefinitionFragment\s*\(\s*'((?:\\'|[^'])+)'\s*\)/g, readDocDefinitionFragment); - // Load the document definitions into the sync function template - var syncFunc = syncFuncTemplate.replace('%SYNC_DOCUMENT_DEFINITIONS%', function() { return syncDocDefn; }); + // Load the document definitions into the sync function template and then escape any occurrence of the backtick character so the sync + // function can be used in a Sync Gateway configuration file multiline string + var syncFunc = syncFuncTemplate.replace('%SYNC_DOCUMENT_DEFINITIONS%', function() { return syncDocDefn; }) + .replace(/`/g, function() { return '\\`'; }); // Normalize code block indentation, normalize line endings and replace blank lines with empty lines syncFunc = indent.indentJS(syncFunc, ' ') diff --git a/etc/test-helper.js b/etc/test-helper.js index 961e7cb2..86fd3510 100644 --- a/etc/test-helper.js +++ b/etc/test-helper.js @@ -257,8 +257,10 @@ var defaultWriteChannel = 'write'; function initSyncFunction(filePath) { // Load the contents of the sync function file into a global variable called syncFunction + var rawSyncFunction = fs.readFileSync(filePath).toString(); + /*jslint evil: true */ - eval('syncFunction = ' + fs.readFileSync(filePath).toString()); + eval('syncFunction = ' + unescapeBackticks(rawSyncFunction)); /*jslint evil: false */ init(); @@ -266,8 +268,10 @@ function initSyncFunction(filePath) { function initDocumentDefinitions(filePath) { // Generate a sync function from the document definitions and load its contents into a global variable called syncFunction + var rawDocDefinitions = syncFunctionLoader.load(filePath); + /*jslint evil: true */ - eval('syncFunction = ' + syncFunctionLoader.load(filePath)); + eval('syncFunction = ' + unescapeBackticks(rawDocDefinitions)); /*jslint evil: false */ init(); @@ -654,3 +658,11 @@ function verifyUnknownDocumentType(doc, oldDoc) { expect(requireAccess.callCount).to.be(0); expect(channel.callCount).to.be(0); } + +// Sync Gateway configuration files use the backtick character to denote the beginning and end of a multiline string. The sync function +// generator script automatically escapes backtick characters with the sequence "\`" so that it produces a valid multiline string. +// However, when loaded by the test helper, a sync function is not inserted into a Sync Gateway configuration file so we must "unescape" +// backtick characters to preserve the original intention. +function unescapeBackticks(originalString) { + return originalString.replace(/\\`/g, function() { return '`'; }); +} diff --git a/package.json b/package.json index b4cce22c..5cbc4fe5 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "devDependencies": { "expect.js": "^0.3.1", "jshint": "^2.9.4", - "mocha": "^3.2.0", + "mocha": "^3.3.0", "simple-mock": "^0.7.3" }, "scripts": { diff --git a/test/hashtable-spec.js b/test/hashtable-spec.js index 59593e0a..84cc2f81 100644 --- a/test/hashtable-spec.js +++ b/test/hashtable-spec.js @@ -145,7 +145,7 @@ describe('Hashtable validation type', function() { var doc = { _id: 'hashtableDoc', staticKeyRegexPatternValidationProp: { - 'Foobar': 'baz', + 'Foo`bar': 'baz', 'Baz': 'qux' } }; @@ -165,7 +165,7 @@ describe('Hashtable validation type', function() { testHelper.verifyDocumentNotCreated( doc, 'hashtableDoc', - errorFormatter.regexPatternHashtableKeyViolation('staticKeyRegexPatternValidationProp[123]', /^[a-zA-Z]+$/)); + errorFormatter.regexPatternHashtableKeyViolation('staticKeyRegexPatternValidationProp[123]', /^[a-zA-Z]+(`[a-zA-Z]+)?$/)); }); }); diff --git a/test/resources/hashtable-doc-definitions.js b/test/resources/hashtable-doc-definitions.js index 43ce1f99..30c6a8b0 100644 --- a/test/resources/hashtable-doc-definitions.js +++ b/test/resources/hashtable-doc-definitions.js @@ -49,7 +49,7 @@ function() { staticKeyRegexPatternValidationProp: { type: 'hashtable', hashtableKeysValidator: { - regexPattern: /^[a-zA-Z]+$/ + regexPattern: /^[a-zA-Z]+(`[a-zA-Z]+)?$/ } }, dynamicKeyRegex: { diff --git a/test/resources/string-doc-definitions.js b/test/resources/string-doc-definitions.js index 31293574..e8ad010b 100644 --- a/test/resources/string-doc-definitions.js +++ b/test/resources/string-doc-definitions.js @@ -48,7 +48,7 @@ function() { }, staticRegexPatternValidationProp: { type: 'string', - regexPattern: /^\d+$/ + regexPattern: /^\d+`[a-z]+$/ }, dynamicRegex: { type: 'string' diff --git a/test/string-spec.js b/test/string-spec.js index 75d0a3aa..99e0e819 100644 --- a/test/string-spec.js +++ b/test/string-spec.js @@ -124,7 +124,7 @@ describe('String validation type', function() { it('allows a doc with a string that matches the expected pattern', function() { var doc = { _id: 'stringDoc', - staticRegexPatternValidationProp: '0472' + staticRegexPatternValidationProp: '0472`foo' }; testHelper.verifyDocumentCreated(doc); @@ -136,7 +136,10 @@ describe('String validation type', function() { staticRegexPatternValidationProp: 'foobar' }; - testHelper.verifyDocumentNotCreated(doc, 'stringDoc', errorFormatter.regexPatternItemViolation('staticRegexPatternValidationProp', /^\d+$/)); + testHelper.verifyDocumentNotCreated( + doc, + 'stringDoc', + errorFormatter.regexPatternItemViolation('staticRegexPatternValidationProp', /^\d+`[a-z]+$/)); }); });