summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean McGivern <sean@gitlab.com>2018-03-28 15:45:16 +0100
committerSean McGivern <sean@gitlab.com>2018-03-29 13:27:08 +0100
commit78aa8c162e229efcde031fc97f3d3ddacfec0806 (patch)
tree95aec4b26e93fa5e840ac502c9620967887ca9ae
parentd7a9df68329bee42a4f2c4934351022c949f7c71 (diff)
downloadgitlab-ce-78aa8c162e229efcde031fc97f3d3ddacfec0806.tar.gz
Escape autocomplete results for Markdown
A label name can contain a `_`, `~~`, or other Markdown-significant characters. But label references are processed _after_ Markdown processing has run, so we can't easily fix this on the backend. We can make it more convenient, though, by changing the frontend to escape these characters so they aren't processed as Markdown, when we insert them from autocomplete.
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js19
-rw-r--r--changelogs/unreleased/escape-autocomplete-values-for-markdown.yml5
-rw-r--r--spec/javascripts/gfm_auto_complete_spec.js10
3 files changed, 28 insertions, 6 deletions
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index 8259133c95b..7e9770a9ea2 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -54,6 +54,7 @@ class GfmAutoComplete {
alias: 'commands',
searchKey: 'search',
skipSpecialCharacterTest: true,
+ skipMarkdownCharacterTest: true,
data: GfmAutoComplete.defaultLoadingData,
displayTpl(value) {
if (GfmAutoComplete.isLoading(value)) return GfmAutoComplete.Loading.template;
@@ -376,15 +377,23 @@ class GfmAutoComplete {
return $.fn.atwho.default.callbacks.filter(query, data, searchKey);
},
beforeInsert(value) {
- let resultantValue = value;
+ let withoutAt = value.substring(1);
+ const at = value.charAt();
+
if (value && !this.setting.skipSpecialCharacterTest) {
- const withoutAt = value.substring(1);
- const regex = value.charAt() === '~' ? /\W|^\d+$/ : /\W/;
+ const regex = at === '~' ? /\W|^\d+$/ : /\W/;
if (withoutAt && regex.test(withoutAt)) {
- resultantValue = `${value.charAt()}"${withoutAt}"`;
+ withoutAt = `"${withoutAt}"`;
}
}
- return resultantValue;
+
+ // We can ignore this for quick actions because they are processed
+ // before Markdown.
+ if (!this.setting.skipMarkdownCharacterTest) {
+ withoutAt = withoutAt.replace(/([~\-_*`])/g, '\\$&');
+ }
+
+ return `${at}${withoutAt}`;
},
matcher(flag, subtext) {
const match = GfmAutoComplete.defaultMatcher(flag, subtext, this.app.controllers);
diff --git a/changelogs/unreleased/escape-autocomplete-values-for-markdown.yml b/changelogs/unreleased/escape-autocomplete-values-for-markdown.yml
new file mode 100644
index 00000000000..eea9da4c579
--- /dev/null
+++ b/changelogs/unreleased/escape-autocomplete-values-for-markdown.yml
@@ -0,0 +1,5 @@
+---
+title: Escape Markdown characters properly when using autocomplete
+merge_request:
+author:
+type: fixed
diff --git a/spec/javascripts/gfm_auto_complete_spec.js b/spec/javascripts/gfm_auto_complete_spec.js
index dc0a5bc275c..1cb20a1e7ff 100644
--- a/spec/javascripts/gfm_auto_complete_spec.js
+++ b/spec/javascripts/gfm_auto_complete_spec.js
@@ -81,13 +81,21 @@ describe('GfmAutoComplete', function () {
});
it('should quote if value contains any non-alphanumeric characters', () => {
- expect(beforeInsert(atwhoInstance, '~label-20')).toBe('~"label-20"');
+ expect(beforeInsert(atwhoInstance, '~label-20')).toBe('~"label\\-20"');
expect(beforeInsert(atwhoInstance, '~label 20')).toBe('~"label 20"');
});
it('should quote integer labels', () => {
expect(beforeInsert(atwhoInstance, '~1234')).toBe('~"1234"');
});
+
+ it('should escape Markdown emphasis characters, except in the first character', () => {
+ expect(beforeInsert(atwhoInstance, '@_group')).toEqual('@\\_group');
+ expect(beforeInsert(atwhoInstance, '~_bug')).toEqual('~\\_bug');
+ expect(beforeInsert(atwhoInstance, '~a `bug`')).toEqual('~"a \\`bug\\`"');
+ expect(beforeInsert(atwhoInstance, '~a ~bug')).toEqual('~"a \\~bug"');
+ expect(beforeInsert(atwhoInstance, '~a **bug')).toEqual('~"a \\*\\*bug"');
+ });
});
describe('DefaultOptions.matcher', function () {