Skip to content

Commit 360b1f4

Browse files
committed
Get app boundary checking preliminarily working
1 parent f68021d commit 360b1f4

File tree

2 files changed

+91
-17
lines changed

2 files changed

+91
-17
lines changed

ember-classic-import-meta-glob/addon/index.js

+61-12
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ import pico from 'picomatch';
1212
* @param {string} modulePath file path prefixed with the module name, not CWD
1313
*/
1414
export function importMetaGlob(glob, options, modulePath) {
15+
let ARITY_3 = arguments.length === 3;
16+
17+
if (typeof options === 'string') {
18+
modulePath = options;
19+
options = null;
20+
}
21+
1522
assert(
1623
`The first argument to import.meta.glob must be a string`,
1724
typeof glob === 'string',
@@ -20,14 +27,18 @@ export function importMetaGlob(glob, options, modulePath) {
2027
`The glob pattern must be a relative path starting with either ./ or ../. Received: ${glob}`,
2128
glob.startsWith('./') || glob.startsWith('../'),
2229
);
23-
assert(
24-
`the second argument to import.meta.glob must be an object`,
25-
typeof options === 'object',
26-
);
27-
assert(
28-
`the only supported option is 'eager'. Received: ${Object.keys(options)}`,
29-
Object.keys(options).length === 1 && 'eager' in options,
30-
);
30+
31+
if (ARITY_3) {
32+
assert(
33+
`the second argument to import.meta.glob must be an object. Received: ${typeof options}`,
34+
typeof options === 'object',
35+
);
36+
assert(
37+
`the only supported option is 'eager'. Received: ${Object.keys(options)}`,
38+
Object.keys(options).length === 1 && 'eager' in options,
39+
);
40+
}
41+
3142
assert(
3243
`the third argument to import.meta.glob must be passed and be the module path. This is filled in automatically via the babel plugin. If you're seeing this something has gone wrong with installing the babel plugin`,
3344
modulePath,
@@ -48,6 +59,12 @@ export function importMetaGlob(glob, options, modulePath) {
4859
let [, ...reversedParts] = modulePath.split('/').reverse();
4960
let currentDir = reversedParts.reverse().join('/');
5061

62+
console.log({ glob, currentDir, modulePath });
63+
assert(
64+
`not a valid path. Received: '${currentDir}' from '${modulePath}'`,
65+
currentDir.length > 0,
66+
);
67+
5168
// TODO: drop the extensions, since at runtime, we don't have them.
5269
let globsArray = Array.isArray(glob) ? glob : [glob];
5370
let fullGlobs = globsArray.map((g) => {
@@ -56,18 +73,50 @@ export function importMetaGlob(glob, options, modulePath) {
5673
let isMatch = pico(fullGlobs);
5774
let matches = allModules.filter(isMatch);
5875

59-
console.log({ fullGlobs, matches, currentDir });
76+
let hasInvalid = fullGlobs.some(isEscapingApp);
6077

61-
// TODO: assert: cannot escape the app.
62-
// (too many ../../../../)
78+
assert(
79+
`Cannot have a path that escapes the app. Received: ${glob}`,
80+
!hasInvalid,
81+
);
6382

6483
let result = {};
6584

6685
for (let match of matches) {
6786
let key = match.replace(`${currentDir}/`, './');
6887

69-
result[key] = requirejs(match);
88+
if (options?.eager) {
89+
result[key] = requirejs(match);
90+
} else {
91+
// TODO: can we usue a real import if we use app-imports
92+
// from ember-auto-import?
93+
result[key] = () => Promise.resolve(requirejs(match));
94+
}
7095
}
7196

7297
return result;
7398
}
99+
100+
function isEscapingApp(path) {
101+
let parts = path.split('/');
102+
103+
let preUpCount = 0;
104+
let upCount = 0;
105+
106+
for (let part of parts) {
107+
if (part === '..') {
108+
upCount++;
109+
continue;
110+
}
111+
112+
if (upCount > 0) {
113+
break;
114+
}
115+
116+
if (part !== '..') {
117+
preUpCount++;
118+
}
119+
}
120+
121+
return upCount >= preUpCount;
122+
}

test-app/tests/glob-test/the-test.js

+30-5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module('Glob tests', function () {
1212
assert.strictEqual(appTreeGlob['./from-app/b'].b, 'b1');
1313
assert.strictEqual(appTreeGlob['./from-app/c'].c, 'c1');
1414
});
15+
1516
test('works from tests', function (assert) {
1617
let keys = Object.keys(testTreeGlob);
1718
assert.deepEqual(keys, [
@@ -25,15 +26,25 @@ module('Glob tests', function () {
2526
});
2627

2728
test('with no matches', function (assert) {
28-
let result = import.meta.glob('/does/not/exist', { eager: true });
29+
let result = import.meta.glob('./does/not/exist', { eager: true });
2930

30-
assert.deepEqual(result, { _: 1 });
31+
assert.strictEqual(Object.keys(result).length, 0);
32+
assert.deepEqual(result, {});
3133
});
3234

3335
test('default (async)', async (assert) => {
3436
let result = import.meta.glob('./from-tests/*');
3537

36-
assert.deepEqual(result, { './from-tests/a': './' });
38+
let keys = Object.keys(result);
39+
assert.deepEqual(keys, [
40+
'./from-tests/a',
41+
'./from-tests/b',
42+
'./from-tests/c',
43+
]);
44+
45+
assert.strictEqual((await result['./from-tests/a']()).a, 'a1');
46+
assert.strictEqual((await result['./from-tests/b']()).b, 'b1');
47+
assert.strictEqual((await result['./from-tests/c']()).c, 'c1');
3748
});
3849

3950
module('underlying runtime', function () {
@@ -48,8 +59,22 @@ module('Glob tests', function () {
4859
module('glob', function () {
4960
test('errors when trying to escape the app', function (assert) {
5061
assert.throws(() => {
51-
importMetaGlob('../../../something', { eager: true }, 'test-app');
52-
}, /123 boop/);
62+
importMetaGlob(
63+
'../../../something',
64+
{ eager: true },
65+
'test-app/tests/glob-test/the-test',
66+
);
67+
}, /not a valid path/);
68+
});
69+
70+
test('errors when a sibling path tries to escape the app', function (assert) {
71+
assert.throws(() => {
72+
importMetaGlob(
73+
'./from-tests/../../../../something',
74+
{ eager: true },
75+
'test-app/tests/glob-test/the-test',
76+
);
77+
}, /Cannot have a path that escapes the app/);
5378
});
5479

5580
test('invalid glob', function (assert) {

0 commit comments

Comments
 (0)