forked from ember-cli/eslint-plugin-ember
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtemplate-no-invalid-link-href.js
More file actions
154 lines (144 loc) · 5.57 KB
/
template-no-invalid-link-href.js
File metadata and controls
154 lines (144 loc) · 5.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
'use strict';
const rule = require('../../../lib/rules/template-no-invalid-link-href');
const RuleTester = require('eslint').RuleTester;
const ruleTester = new RuleTester({
parser: require.resolve('ember-eslint-parser'),
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
});
ruleTester.run('template-no-invalid-link-href', rule, {
valid: [
// Valid navigable hrefs.
'<template><a href="/x">Link</a></template>',
'<template><a href="https://example.com">Link</a></template>',
'<template><a href="#section">Link</a></template>',
'<template><a href="mailto:[email protected]">Email</a></template>',
'<template><a href="tel:+47123">Phone</a></template>',
// Dynamic href — rule can't statically validate, skips.
'<template><a href={{this.url}}>Link</a></template>',
'<template><a href="{{this.prefix}}/{{this.slug}}">Link</a></template>',
// No href at all — handled by template-link-href-attributes, not this rule.
'<template><a>Not a link</a></template>',
// Non-anchor elements are not in scope.
'<template><button>Click me</button></template>',
'<template><div href="#">Not an anchor</div></template>',
// <area> is in scope — same href semantics as <a>. Valid values pass.
'<template><map name="m"><area href="/region-a" shape="rect" coords="0,0,10,10" /></map></template>',
'<template><area href="#section" shape="default" /></template>',
// Dynamic area href — skip.
'<template><area href={{this.url}} shape="rect" coords="0,0,1,1" /></template>',
// Non-scheme URLs that happen to contain `javascript:` are not javascript:
// URLs — they are relative paths or fragments. The URL parser resolves
// them against the base URL; no script runs.
'<template><a href="./javascript:foo">Relative path</a></template>',
'<template><a href="#javascript:foo">Fragment id</a></template>',
'<template><a href="/javascript:foo">Absolute path</a></template>',
'<template><a href="?q=javascript:foo">Query string</a></template>',
// `javascript` as a bare word (no colon) — treated as a relative path.
// Our regex only matches the `javascript:` scheme (with colon), so these pass.
'<template><a href="javascript">Relative</a></template>',
'<template><a href="javascriptFoo">Relative</a></template>',
],
invalid: [
// Plain "#" placeholder.
{
code: '<template><a href="#">Click</a></template>',
output: null,
errors: [{ messageId: 'invalidHref' }],
},
{
code: '<template><a href="#!">Click</a></template>',
output: null,
errors: [{ messageId: 'invalidHref' }],
},
// Empty / whitespace href.
{
code: '<template><a href="">Click</a></template>',
output: null,
errors: [{ messageId: 'invalidHref' }],
},
{
code: '<template><a href=" ">Click</a></template>',
output: null,
errors: [{ messageId: 'invalidHref' }],
},
{
code: '<template><a href>Click</a></template>',
output: null,
errors: [{ messageId: 'invalidHref' }],
},
// javascript: protocol.
{
code: '<template><a href="javascript:void(0)">Click</a></template>',
output: null,
errors: [{ messageId: 'invalidHref' }],
},
{
code: '<template><a href="JavaScript:alert(1)">Click</a></template>',
output: null,
errors: [{ messageId: 'invalidHref' }],
},
// Leading whitespace — catches obfuscations.
{
code: '<template><a href=" javascript:void(0)">Click</a></template>',
output: null,
errors: [{ messageId: 'invalidHref' }],
},
// Mustache-string-literal hrefs resolve to their static value via the
// shared `getStaticAttrValue` helper — the rule validates them the same
// as text-node values. Covers the common bypass hole where authors
// wrap a literal href in mustaches (`{{"#"}}`) to dodge a simple
// "is this a text node" check.
{
code: '<template><a href={{"#"}}>Click</a></template>',
output: null,
errors: [{ messageId: 'invalidHref' }],
},
{
code: '<template><a href={{"javascript:void(0)"}}>Click</a></template>',
output: null,
errors: [{ messageId: 'invalidHref' }],
},
// Single-part quoted-mustache (GlimmerConcatStatement wrapping a
// literal) resolves the same way.
{
code: '<template><a href="{{\'#\'}}">Click</a></template>',
output: null,
errors: [{ messageId: 'invalidHref' }],
},
// <area> shares <a>'s href semantics — same invalid values flag.
{
code: '<template><area href="#" shape="rect" coords="0,0,10,10" /></template>',
output: null,
errors: [{ messageId: 'invalidHref' }],
},
{
code: '<template><area href="" shape="rect" coords="0,0,10,10" /></template>',
output: null,
errors: [{ messageId: 'invalidHref' }],
},
{
code: '<template><area href="javascript:alert(1)" shape="rect" coords="0,0,10,10" /></template>',
output: null,
errors: [{ messageId: 'invalidHref' }],
},
],
});
const hbsRuleTester = new RuleTester({
parser: require.resolve('ember-eslint-parser/hbs'),
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
});
hbsRuleTester.run('template-no-invalid-link-href', rule, {
valid: ['<a href="/x">Link</a>', '<a href={{this.url}}>Link</a>', '<a>Not a link</a>'],
invalid: [
{
code: '<a href="#">Click</a>',
output: null,
errors: [{ messageId: 'invalidHref' }],
},
{
code: '<a href="javascript:void(0)">Click</a>',
output: null,
errors: [{ messageId: 'invalidHref' }],
},
],
});