UNPKG

3.42 kBJavaScriptView Raw
1/**
2 * @fileoverview HTML special characters should be escaped.
3 * @author Patrick Hayes
4 */
5
6'use strict';
7
8const docsUrl = require('../util/docsUrl');
9const jsxUtil = require('../util/jsx');
10
11// ------------------------------------------------------------------------------
12// Rule Definition
13// ------------------------------------------------------------------------------
14
15// NOTE: '<' and '{' are also problematic characters, but they do not need
16// to be included here because it is a syntax error when these characters are
17// included accidentally.
18const DEFAULTS = [{
19 char: '>',
20 alternatives: ['&gt;']
21}, {
22 char: '"',
23 alternatives: ['&quot;', '&ldquo;', '&#34;', '&rdquo;']
24}, {
25 char: '\'',
26 alternatives: ['&apos;', '&lsquo;', '&#39;', '&rsquo;']
27}, {
28 char: '}',
29 alternatives: ['&#125;']
30}];
31
32module.exports = {
33 meta: {
34 docs: {
35 description: 'Detect unescaped HTML entities, which might represent malformed tags',
36 category: 'Possible Errors',
37 recommended: true,
38 url: docsUrl('no-unescaped-entities')
39 },
40 schema: [{
41 type: 'object',
42 properties: {
43 forbid: {
44 type: 'array',
45 items: {
46 oneOf: [{
47 type: 'string'
48 }, {
49 type: 'object',
50 properties: {
51 char: {
52 type: 'string'
53 },
54 alternatives: {
55 type: 'array',
56 uniqueItems: true,
57 items: {
58 type: 'string'
59 }
60 }
61 }
62 }]
63 }
64 }
65 },
66 additionalProperties: false
67 }]
68 },
69
70 create(context) {
71 function reportInvalidEntity(node) {
72 const configuration = context.options[0] || {};
73 const entities = configuration.forbid || DEFAULTS;
74
75 // HTML entites are already escaped in node.value (as well as node.raw),
76 // so pull the raw text from context.getSourceCode()
77 for (let i = node.loc.start.line; i <= node.loc.end.line; i++) {
78 let rawLine = context.getSourceCode().lines[i - 1];
79 let start = 0;
80 let end = rawLine.length;
81 if (i === node.loc.start.line) {
82 start = node.loc.start.column;
83 }
84 if (i === node.loc.end.line) {
85 end = node.loc.end.column;
86 }
87 rawLine = rawLine.substring(start, end);
88 for (let j = 0; j < entities.length; j++) {
89 for (let index = 0; index < rawLine.length; index++) {
90 const c = rawLine[index];
91 if (typeof entities[j] === 'string') {
92 if (c === entities[j]) {
93 context.report({
94 loc: {line: i, column: start + index},
95 message: `HTML entity, \`${entities[j]}\` , must be escaped.`,
96 node
97 });
98 }
99 } else if (c === entities[j].char) {
100 context.report({
101 loc: {line: i, column: start + index},
102 message: `\`${entities[j].char}\` can be escaped with ${entities[j].alternatives.map((alt) => `\`${alt}\``).join(', ')}.`,
103 node
104 });
105 }
106 }
107 }
108 }
109 }
110
111 return {
112 'Literal, JSXText'(node) {
113 if (jsxUtil.isJSX(node.parent)) {
114 reportInvalidEntity(node);
115 }
116 }
117 };
118 }
119};