Skip to content

Commit 8c8af7c

Browse files
Merge pull request #2579 from NullVoxPopuli/nvp/template-lint-extract-rule-template-no-triple-curlies
Extract rule: template-no-triple-curlies
2 parents 58f47d6 + cb32eae commit 8c8af7c

4 files changed

Lines changed: 116 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,7 @@ rules in templates can be disabled with eslint directives with mustache or html
427427
| Name                       | Description | 💼 | 🔧 | 💡 |
428428
| :--------------------------------------------------------------------- | :-------------------------------------------------------------- | :- | :- | :- |
429429
| [template-link-rel-noopener](docs/rules/template-link-rel-noopener.md) | require rel="noopener noreferrer" on links with target="_blank" | | 🔧 | |
430+
| [template-no-triple-curlies](docs/rules/template-no-triple-curlies.md) | disallow usage of triple curly brackets (unescaped variables) | | | |
430431

431432
### Services
432433

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# ember/template-no-triple-curlies
2+
3+
<!-- end auto-generated rule header -->
4+
5+
Usage of triple curly braces to allow raw HTML to be injected into the DOM is a large vector for exploits of your application (especially when the raw HTML is user-controllable). Instead of using `{{{foo}}}`, you should use appropriate helpers or computed properties that return a `SafeString` (via `Ember.String.htmlSafe` generally) and ensure that user-supplied data is properly escaped.
6+
7+
## Examples
8+
9+
This rule **forbids** the following:
10+
11+
```gjs
12+
<template>
13+
{{{foo}}}
14+
</template>
15+
```
16+
17+
This rule **allows** the following:
18+
19+
```gjs
20+
<template>
21+
{{foo}}
22+
</template>
23+
```
24+
25+
## References
26+
27+
- See the [documentation](https://api.emberjs.com/ember/release/functions/@ember%2Ftemplate/htmlSafe) for Ember's `htmlSafe` function
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/** @type {import('eslint').Rule.RuleModule} */
2+
module.exports = {
3+
meta: {
4+
type: 'problem',
5+
docs: {
6+
description: 'disallow usage of triple curly brackets (unescaped variables)',
7+
category: 'Security',
8+
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-triple-curlies.md',
9+
templateMode: 'both',
10+
},
11+
schema: [],
12+
messages: { unsafe: 'Usage of triple curly brackets is unsafe' },
13+
originallyFrom: {
14+
name: 'ember-template-lint',
15+
rule: 'lib/rules/no-triple-curlies.js',
16+
docs: 'docs/rule/no-triple-curlies.md',
17+
tests: 'test/unit/rules/no-triple-curlies-test.js',
18+
},
19+
},
20+
21+
create(context) {
22+
return {
23+
GlimmerMustacheStatement(node) {
24+
if (node.trusting === true) {
25+
context.report({
26+
node,
27+
messageId: 'unsafe',
28+
});
29+
}
30+
},
31+
};
32+
},
33+
};
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//------------------------------------------------------------------------------
2+
// Requirements
3+
//------------------------------------------------------------------------------
4+
5+
const rule = require('../../../lib/rules/template-no-triple-curlies');
6+
const RuleTester = require('eslint').RuleTester;
7+
8+
const validHbs = [
9+
'{{foo}}',
10+
'{{! template-lint-disable no-bare-strings }}',
11+
'{{! template-lint-disable }}',
12+
// Upstream also treats `{{! template-lint-disable no-triple-curlies}}{{{lol}}}` as valid,
13+
// but this RuleTester does not honor template-lint disable directives.
14+
];
15+
16+
const invalidHbs = [
17+
{
18+
code: '\n {{{foo}}}',
19+
output: null,
20+
errors: [{ message: 'Usage of triple curly brackets is unsafe' }],
21+
},
22+
];
23+
24+
function wrapTemplate(entry) {
25+
if (typeof entry === 'string') {
26+
return `<template>${entry}</template>`;
27+
}
28+
29+
return {
30+
...entry,
31+
code: `<template>${entry.code}</template>`,
32+
output: entry.output ? `<template>${entry.output}</template>` : entry.output,
33+
errors: entry.errors.map(() => ({ messageId: 'unsafe' })),
34+
};
35+
}
36+
37+
const gjsRuleTester = new RuleTester({
38+
parser: require.resolve('ember-eslint-parser'),
39+
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
40+
});
41+
42+
gjsRuleTester.run('template-no-triple-curlies', rule, {
43+
valid: validHbs.map(wrapTemplate),
44+
invalid: invalidHbs.map(wrapTemplate),
45+
});
46+
47+
const hbsRuleTester = new RuleTester({
48+
parser: require.resolve('ember-eslint-parser/hbs'),
49+
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
50+
});
51+
52+
hbsRuleTester.run('template-no-triple-curlies', rule, {
53+
valid: validHbs,
54+
invalid: invalidHbs,
55+
});

0 commit comments

Comments
 (0)