Skip to content

Commit 98356b7

Browse files
committed
fix: warn on bidirectional control characters
1 parent 0ace76d commit 98356b7

File tree

14 files changed

+179
-1
lines changed

14 files changed

+179
-1
lines changed

.changeset/rare-crews-collect.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: warn on bidirectional control characters

documentation/docs/98-reference/.generated/compile-warnings.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,14 @@ Attributes should not contain ':' characters to prevent ambiguity with Svelte di
586586
Quoted attributes on components and custom elements will be stringified in a future version of Svelte. If this isn't what you want, remove the quotes
587587
```
588588

589+
### bidirectional_control_characters_detected
590+
591+
```
592+
A bidirectional control character was detected in your code. These characters can be used to alter the visual direction of your code and could have unintended consequences
593+
```
594+
595+
Bidirectional control characters can alter the direction in which text appears to be in. For example, via control characters, you can make `defabc` look like `abcdef`. As a result, if you were to copy some code that has these control characters, they may alter the behavior of your code in ways you did not intend. For more information check out [this website](https://trojansource.codes).
596+
589597
### bind_invalid_each_rest
590598

591599
```

packages/svelte/messages/compile-warnings/misc.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## bidirectional_control_characters_detected
2+
3+
> A bidirectional control character was detected in your code. These characters can be used to alter the visual direction of your code and could have unintended consequences
4+
5+
Bidirectional control characters can alter the direction in which text appears to be in. For example, via control characters, you can make `defabc` look like `abcdef`. As a result, if you were to unknowingly copy and paste some code that has these control characters, they may alter the behavior of your code in ways you did not intend. For more information check out [this website](https://trojansource.codes).
6+
17
## legacy_code
28

39
> `%code%` is no longer valid — please use `%suggestion%` instead

packages/svelte/src/compiler/phases/1-parse/index.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
// @ts-expect-error acorn type definitions are borked in the release we use
33
import { isIdentifierStart, isIdentifierChar } from 'acorn';
44
import fragment from './state/fragment.js';
5-
import { regex_whitespace } from '../patterns.js';
5+
import { regex_bidirectional_control_characters, regex_whitespace } from '../patterns.js';
66
import * as e from '../../errors.js';
7+
import * as w from '../../warnings.js';
78
import { create_fragment } from './utils/create.js';
89
import read_options from './read/options.js';
910
import { is_reserved } from '../../../utils.js';
@@ -64,6 +65,11 @@ export class Parser {
6465
throw new TypeError('Template must be a string');
6566
}
6667

68+
const control_chars = regex_bidirectional_control_characters.exec(template);
69+
if (control_chars) {
70+
w.bidirectional_control_characters_detected({ start: template.indexOf(control_chars[0][0]) });
71+
}
72+
6773
this.loose = loose;
6874
this.template_untrimmed = template;
6975
this.template = template.trimEnd();

packages/svelte/src/compiler/phases/2-analyze/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import { ImportDeclaration } from './visitors/ImportDeclaration.js';
4343
import { KeyBlock } from './visitors/KeyBlock.js';
4444
import { LabeledStatement } from './visitors/LabeledStatement.js';
4545
import { LetDirective } from './visitors/LetDirective.js';
46+
import { Literal } from './visitors/Literal.js';
4647
import { MemberExpression } from './visitors/MemberExpression.js';
4748
import { NewExpression } from './visitors/NewExpression.js';
4849
import { OnDirective } from './visitors/OnDirective.js';
@@ -63,6 +64,7 @@ import { SvelteSelf } from './visitors/SvelteSelf.js';
6364
import { SvelteWindow } from './visitors/SvelteWindow.js';
6465
import { SvelteBoundary } from './visitors/SvelteBoundary.js';
6566
import { TaggedTemplateExpression } from './visitors/TaggedTemplateExpression.js';
67+
import { TemplateElement } from './visitors/TemplateElement.js';
6668
import { Text } from './visitors/Text.js';
6769
import { TitleElement } from './visitors/TitleElement.js';
6870
import { TransitionDirective } from './visitors/TransitionDirective.js';
@@ -156,6 +158,7 @@ const visitors = {
156158
KeyBlock,
157159
LabeledStatement,
158160
LetDirective,
161+
Literal,
159162
MemberExpression,
160163
NewExpression,
161164
OnDirective,
@@ -176,6 +179,7 @@ const visitors = {
176179
SvelteWindow,
177180
SvelteBoundary,
178181
TaggedTemplateExpression,
182+
TemplateElement,
179183
Text,
180184
TransitionDirective,
181185
TitleElement,
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/** @import { Literal } from 'estree' */
2+
import * as w from '../../../warnings.js';
3+
import { regex_bidirectional_control_characters } from '../../patterns.js';
4+
5+
/**
6+
* @param {Literal} node
7+
*/
8+
export function Literal(node) {
9+
if (typeof node.value === 'string') {
10+
if (regex_bidirectional_control_characters.test(node.value)) {
11+
w.bidirectional_control_characters_detected(node);
12+
}
13+
}
14+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/** @import { TemplateElement } from 'estree' */
2+
import * as w from '../../../warnings.js';
3+
import { regex_bidirectional_control_characters } from '../../patterns.js';
4+
5+
/**
6+
* @param {TemplateElement} node
7+
*/
8+
export function TemplateElement(node) {
9+
if (regex_bidirectional_control_characters.test(node.value.cooked ?? '')) {
10+
w.bidirectional_control_characters_detected(node);
11+
}
12+
}

packages/svelte/src/compiler/phases/patterns.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ export const regex_invalid_identifier_chars = /(^[^a-zA-Z_$]|[^a-zA-Z0-9_$])/g;
2121
export const regex_starts_with_vowel = /^[aeiou]/;
2222
export const regex_heading_tags = /^h[1-6]$/;
2323
export const regex_illegal_attribute_character = /(^[0-9-.])|[\^$@%&#?!|()[\]{}^*+~;]/;
24+
export const regex_bidirectional_control_characters = /[\u202a\u202b\u202c\u202d\u202e\u2066\u2067\u2068\u2069]/;

packages/svelte/src/compiler/warnings.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ export const codes = [
8686
'a11y_role_supports_aria_props_implicit',
8787
'a11y_unknown_aria_attribute',
8888
'a11y_unknown_role',
89+
'bidirectional_control_characters_detected',
8990
'legacy_code',
9091
'unknown_code',
9192
'options_deprecated_accessors',
@@ -506,6 +507,14 @@ export function a11y_unknown_role(node, role, suggestion) {
506507
w(node, 'a11y_unknown_role', `${suggestion ? `Unknown role '${role}'. Did you mean '${suggestion}'?` : `Unknown role '${role}'`}\nhttps://svelte.dev/e/a11y_unknown_role`);
507508
}
508509

510+
/**
511+
* A bidirectional control character was detected in your code. These characters can be used to alter the visual direction of your code and could have unintended consequences
512+
* @param {null | NodeLike} node
513+
*/
514+
export function bidirectional_control_characters_detected(node) {
515+
w(node, 'bidirectional_control_characters_detected', `A bidirectional control character was detected in your code. These characters can be used to alter the visual direction of your code and could have unintended consequences\nhttps://svelte.dev/e/bidirectional_control_characters_detected`);
516+
}
517+
509518
/**
510519
* `%code%` is no longer valid — please use `%suggestion%` instead
511520
* @param {null | NodeLike} node
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import 'svelte/internal/disclose-version';
2+
import 'svelte/internal/flags/legacy';
3+
import * as $ from 'svelte/internal/client';
4+
5+
var root = $.template(`⁧⁦def⁩⁦abc⁩⁩ <h1></h1>`, 1);
6+
7+
export default function Bidirectional_control_chars($$anchor) {
8+
let name = '\u2067\u2066rld\u2069\u2066wo\u2069\u2069';
9+
10+
$.next();
11+
12+
var fragment = root();
13+
var h1 = $.sibling($.first_child(fragment));
14+
15+
h1.textContent = 'Hello, ⁧⁦rld⁩⁦wo⁩⁩!';
16+
$.append($$anchor, fragment);
17+
}

0 commit comments

Comments
 (0)