Skip to content

Commit aef04e5

Browse files
committed
prevent apply/call/bind
1 parent 576d07e commit aef04e5

File tree

7 files changed

+44
-23
lines changed

7 files changed

+44
-23
lines changed

packages/svelte/src/compiler/errors.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ const parse = {
9191
`A component can have a single top-level <script> element and/or a single top-level <script context="module"> element`,
9292
'invalid-render-expression': () => '{@render ...} tags can only contain call expressions',
9393
'invalid-render-arguments': () => 'expected at most one argument',
94+
'invalid-render-call': () =>
95+
'Calling a snippet function using apply, bind or call is not allowed',
9496
'invalid-render-spread-argument': () => 'cannot use spread arguments in {@render ...} tags',
9597
'invalid-snippet-rest-parameter': () =>
9698
'snippets do not support rest parameters; use an array instead'

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import {
44
get_parent,
55
is_expression_attribute,
66
is_text_attribute,
7-
object
7+
object,
8+
unwrap_optional
89
} from '../../utils/ast.js';
910
import { warn } from '../../warnings.js';
1011
import fuzzymatch from '../1-parse/utils/fuzzymatch.js';
@@ -604,16 +605,22 @@ const validation = {
604605
});
605606
},
606607
RenderTag(node, context) {
607-
const raw_args =
608-
node.expression.type === 'CallExpression'
609-
? node.expression.arguments
610-
: node.expression.expression.arguments;
608+
const raw_args = unwrap_optional(node.expression).arguments;
611609
for (const arg of raw_args) {
612610
if (arg.type === 'SpreadElement') {
613611
error(arg, 'invalid-render-spread-argument');
614612
}
615613
}
616614

615+
const callee = unwrap_optional(node.expression).callee;
616+
if (
617+
callee.type === 'MemberExpression' &&
618+
callee.property.type === 'Identifier' &&
619+
['bind', 'apply', 'call'].includes(callee.property.name)
620+
) {
621+
error(node, 'invalid-render-call');
622+
}
623+
617624
const is_inside_textarea = context.path.find((n) => {
618625
return (
619626
n.type === 'SvelteElement' &&

packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import {
33
extract_paths,
44
is_event_attribute,
55
is_text_attribute,
6-
object
6+
object,
7+
unwrap_optional
78
} from '../../../../utils/ast.js';
89
import { binding_properties } from '../../../bindings.js';
910
import {
@@ -1864,14 +1865,8 @@ export const template_visitors = {
18641865
},
18651866
RenderTag(node, context) {
18661867
context.state.template.push('<!>');
1867-
const callee =
1868-
node.expression.type === 'CallExpression'
1869-
? node.expression.callee
1870-
: node.expression.expression.callee;
1871-
const raw_args =
1872-
node.expression.type === 'CallExpression'
1873-
? node.expression.arguments
1874-
: node.expression.expression.arguments;
1868+
const callee = unwrap_optional(node.expression).callee;
1869+
const raw_args = unwrap_optional(node.expression).arguments;
18751870
const is_reactive =
18761871
callee.type !== 'Identifier' || context.state.scope.get(callee.name)?.kind !== 'normal';
18771872

packages/svelte/src/compiler/phases/3-transform/server/transform-server.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { walk } from 'zimmerframe';
22
import { set_scope, get_rune } from '../../scope.js';
3-
import { extract_identifiers, extract_paths, is_event_attribute } from '../../../utils/ast.js';
3+
import {
4+
extract_identifiers,
5+
extract_paths,
6+
is_event_attribute,
7+
unwrap_optional
8+
} from '../../../utils/ast.js';
49
import * as b from '../../../utils/builders.js';
510
import is_reference from 'is-reference';
611
import {
@@ -1141,14 +1146,8 @@ const template_visitors = {
11411146
state.init.push(anchor);
11421147
state.template.push(t_expression(anchor_id));
11431148

1144-
const callee =
1145-
node.expression.type === 'CallExpression'
1146-
? node.expression.callee
1147-
: node.expression.expression.callee;
1148-
const raw_args =
1149-
node.expression.type === 'CallExpression'
1150-
? node.expression.arguments
1151-
: node.expression.expression.arguments;
1149+
const callee = unwrap_optional(node.expression).callee;
1150+
const raw_args = unwrap_optional(node.expression).arguments;
11521151

11531152
const expression = /** @type {import('estree').Expression} */ (context.visit(callee));
11541153
const snippet_function = state.options.dev

packages/svelte/src/compiler/utils/ast.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,3 +361,12 @@ export function is_simple_expression(node) {
361361

362362
return false;
363363
}
364+
365+
/**
366+
* @template {import('estree').SimpleCallExpression | import('estree').MemberExpression} T
367+
* @param {import('estree').ChainExpression & { expression : T } | T} node
368+
* @returns {T}
369+
*/
370+
export function unwrap_optional(node) {
371+
return node.type === 'ChainExpression' ? node.expression : node;
372+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
error: {
5+
code: 'invalid-render-call',
6+
message: 'Calling a snippet function using apply, bind or call is not allowed'
7+
}
8+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{@render snippet.apply(null, [1, 2, 3])}

0 commit comments

Comments
 (0)