@hyperfrontend/project-scope 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/_shared/core/fs/guard/index.cjs.js +7 -0
  3. package/_shared/core/fs/guard/index.esm.js +5 -0
  4. package/_shared/core/fs/stat/index.cjs.js +3 -2
  5. package/_shared/core/fs/stat/index.esm.js +3 -2
  6. package/_shared/core/path/confine/index.cjs.js +22 -0
  7. package/_shared/core/path/confine/index.esm.js +19 -0
  8. package/cli/index.cjs.js +5 -1
  9. package/cli/index.esm.js +5 -1
  10. package/core/fs/index.cjs.js +12 -2
  11. package/core/fs/index.esm.js +12 -2
  12. package/core/index.cjs.js +13 -3
  13. package/core/index.esm.js +14 -4
  14. package/core/path/index.cjs.js +4 -2
  15. package/core/path/index.d.ts +22 -1
  16. package/core/path/index.d.ts.map +1 -1
  17. package/core/path/index.esm.js +4 -3
  18. package/heuristics/dependencies/index.cjs.js +10 -1
  19. package/heuristics/dependencies/index.esm.js +12 -3
  20. package/heuristics/entry-points/index.cjs.js +2 -1
  21. package/heuristics/entry-points/index.esm.js +2 -1
  22. package/heuristics/framework/index.cjs.js +2 -1
  23. package/heuristics/framework/index.esm.js +2 -1
  24. package/heuristics/index.cjs.js +10 -1
  25. package/heuristics/index.esm.js +12 -3
  26. package/heuristics/project-type/index.cjs.js +2 -1
  27. package/heuristics/project-type/index.esm.js +2 -1
  28. package/index.cjs.js +18 -3
  29. package/index.esm.js +19 -4
  30. package/nx/index.cjs.js +3 -2
  31. package/nx/index.esm.js +3 -2
  32. package/package.json +1 -1
  33. package/project/config/index.cjs.js +5 -1
  34. package/project/config/index.esm.js +5 -1
  35. package/project/index.cjs.js +5 -1
  36. package/project/index.esm.js +5 -1
  37. package/project/package/index.cjs.js +5 -1
  38. package/project/package/index.esm.js +5 -1
  39. package/project/root/index.cjs.js +2 -1
  40. package/project/root/index.esm.js +2 -1
  41. package/project/traversal/index.cjs.js +2 -1
  42. package/project/traversal/index.esm.js +2 -1
  43. package/tech/backend/index.cjs.js +2 -1
  44. package/tech/backend/index.esm.js +2 -1
  45. package/tech/build/index.cjs.js +2 -1
  46. package/tech/build/index.esm.js +2 -1
  47. package/tech/frontend/index.cjs.js +2 -1
  48. package/tech/frontend/index.esm.js +2 -1
  49. package/tech/index.cjs.js +2 -1
  50. package/tech/index.esm.js +2 -1
  51. package/tech/legacy/index.cjs.js +2 -1
  52. package/tech/legacy/index.esm.js +2 -1
  53. package/tech/linting/index.cjs.js +2 -1
  54. package/tech/linting/index.esm.js +2 -1
  55. package/tech/monorepo/index.cjs.js +2 -1
  56. package/tech/monorepo/index.esm.js +2 -1
  57. package/tech/testing/index.cjs.js +2 -1
  58. package/tech/testing/index.esm.js +2 -1
  59. package/tech/types/index.cjs.js +2 -1
  60. package/tech/types/index.esm.js +2 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.2.3](https://github.com/AndrewRedican/hyperfrontend/compare/5f116abb8ba6355dfb283fa03b7481e5eb029480...7fb26b6a30c007cd0392f1fc973265c3d25e16dd) - 2026-06-28
6
+
7
+ ### Bug Fixes
8
+
9
+ - confine import resolution to the project root
10
+ - reject NUL-byte paths in fs primitives
11
+
5
12
  ## [0.2.2](https://github.com/AndrewRedican/hyperfrontend/compare/d96fee4d4d3a70178c8a01e5f2e2ae675fa23f37...466c0388c4cd516b9c704214140b4df1004098e6) - 2026-06-23
6
13
 
7
14
  ### Other
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ function isSafePath(filePath) {
4
+ return !filePath.includes('\u0000');
5
+ }
6
+
7
+ exports.isSafePath = isSafePath;
@@ -0,0 +1,5 @@
1
+ function isSafePath(filePath) {
2
+ return !filePath.includes('\u0000');
3
+ }
4
+
5
+ export { isSafePath };
@@ -1,9 +1,10 @@
1
1
  'use strict';
2
2
 
3
+ const { isSafePath } = require('../guard/index.cjs.js');
3
4
  const node_fs = require('node:fs');
4
5
 
5
6
  function getFileStat(filePath, followSymlinks = true) {
6
- if (!node_fs.existsSync(filePath)) {
7
+ if (!isSafePath(filePath) || !node_fs.existsSync(filePath)) {
7
8
  return null;
8
9
  }
9
10
  try {
@@ -36,7 +37,7 @@ function isSymlink(linkPath) {
36
37
  return stats?.isSymlink ?? false;
37
38
  }
38
39
  function exists(filePath) {
39
- return node_fs.existsSync(filePath);
40
+ return isSafePath(filePath) && node_fs.existsSync(filePath);
40
41
  }
41
42
 
42
43
  exports.getFileStat = getFileStat;
@@ -1,7 +1,8 @@
1
+ import { isSafePath } from '../guard/index.esm.js';
1
2
  import { existsSync, statSync, lstatSync } from 'node:fs';
2
3
 
3
4
  function getFileStat(filePath, followSymlinks = true) {
4
- if (!existsSync(filePath)) {
5
+ if (!isSafePath(filePath) || !existsSync(filePath)) {
5
6
  return null;
6
7
  }
7
8
  try {
@@ -34,7 +35,7 @@ function isSymlink(linkPath) {
34
35
  return stats?.isSymlink ?? false;
35
36
  }
36
37
  function exists(filePath) {
37
- return existsSync(filePath);
38
+ return isSafePath(filePath) && existsSync(filePath);
38
39
  }
39
40
 
40
41
  export { getFileStat, isFile, isDirectory, isSymlink, exists };
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ const { removeTrailingSlash, normalizePath } = require('../normalize/index.cjs.js');
4
+ const node_path = require('node:path');
5
+ const node_fs = require('node:fs');
6
+
7
+ function contains(root, path) {
8
+ const base = removeTrailingSlash(root);
9
+ return path === base || path.startsWith(`${base}/`);
10
+ }
11
+ function isWithinRoot(root, candidate) {
12
+ if (!contains(normalizePath(node_path.resolve(root)), normalizePath(node_path.resolve(candidate)))) {
13
+ return false;
14
+ }
15
+ if (!node_fs.existsSync(candidate)) {
16
+ return true;
17
+ }
18
+ return contains(normalizePath(node_fs.realpathSync(root)), normalizePath(node_fs.realpathSync(candidate)));
19
+ }
20
+
21
+ exports.contains = contains;
22
+ exports.isWithinRoot = isWithinRoot;
@@ -0,0 +1,19 @@
1
+ import { removeTrailingSlash, normalizePath } from '../normalize/index.esm.js';
2
+ import { existsSync, realpathSync } from 'node:fs';
3
+ import { resolve } from 'node:path';
4
+
5
+ function contains(root, path) {
6
+ const base = removeTrailingSlash(root);
7
+ return path === base || path.startsWith(`${base}/`);
8
+ }
9
+ function isWithinRoot(root, candidate) {
10
+ if (!contains(normalizePath(resolve(root)), normalizePath(resolve(candidate)))) {
11
+ return false;
12
+ }
13
+ if (!existsSync(candidate)) {
14
+ return true;
15
+ }
16
+ return contains(normalizePath(realpathSync(root)), normalizePath(realpathSync(candidate)));
17
+ }
18
+
19
+ export { contains, isWithinRoot };
package/cli/index.cjs.js CHANGED
@@ -14,6 +14,7 @@ const index_cjs_js$6 = require('../_dependencies/@hyperfrontend/immutable-api-ut
14
14
  const index_cjs_js$7 = require('../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/map/index.cjs.js');
15
15
  const index_cjs_js$8 = require('../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/math/index.cjs.js');
16
16
  const index_cjs_js$9 = require('../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/number/index.cjs.js');
17
+ const { isSafePath } = require('../_shared/core/fs/guard/index.cjs.js');
17
18
  const { getFileStat, isDirectory, exists } = require('../_shared/core/fs/stat/index.cjs.js');
18
19
  const { join } = require('../_shared/core/path/join/index.cjs.js');
19
20
  const { createConfigError } = require('../_shared/core/errors/structured-errors/index.cjs.js');
@@ -233,6 +234,9 @@ function createFileSystemError(message, code, context) {
233
234
  * ```
234
235
  */
235
236
  function readFileContent(filePath, encoding = 'utf-8') {
237
+ if (!isSafePath(filePath)) {
238
+ throw createFileSystemError(`Unsafe file path: ${filePath}`, 'FS_READ_ERROR', { path: filePath, operation: 'read' });
239
+ }
236
240
  if (!node_fs.existsSync(filePath)) {
237
241
  fsLogger.debug('File not found', { path: filePath });
238
242
  throw createFileSystemError(`File not found: ${filePath}`, 'FS_NOT_FOUND', { path: filePath, operation: 'read' });
@@ -261,7 +265,7 @@ function readFileContent(filePath, encoding = 'utf-8') {
261
265
  * ```
262
266
  */
263
267
  function readFileIfExists(filePath, encoding = 'utf-8') {
264
- if (!node_fs.existsSync(filePath)) {
268
+ if (!isSafePath(filePath) || !node_fs.existsSync(filePath)) {
265
269
  return null;
266
270
  }
267
271
  try {
package/cli/index.esm.js CHANGED
@@ -12,6 +12,7 @@ import { createError } from '../_dependencies/@hyperfrontend/immutable-api-utils
12
12
  import { createMap } from '../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/map/index.esm.js';
13
13
  import { min, round } from '../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/math/index.esm.js';
14
14
  import { parseInt, parseFloat } from '../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/number/index.esm.js';
15
+ import { isSafePath } from '../_shared/core/fs/guard/index.esm.js';
15
16
  import { getFileStat, isDirectory, exists } from '../_shared/core/fs/stat/index.esm.js';
16
17
  import { join } from '../_shared/core/path/join/index.esm.js';
17
18
  import { createConfigError } from '../_shared/core/errors/structured-errors/index.esm.js';
@@ -231,6 +232,9 @@ function createFileSystemError(message, code, context) {
231
232
  * ```
232
233
  */
233
234
  function readFileContent(filePath, encoding = 'utf-8') {
235
+ if (!isSafePath(filePath)) {
236
+ throw createFileSystemError(`Unsafe file path: ${filePath}`, 'FS_READ_ERROR', { path: filePath, operation: 'read' });
237
+ }
234
238
  if (!existsSync(filePath)) {
235
239
  fsLogger.debug('File not found', { path: filePath });
236
240
  throw createFileSystemError(`File not found: ${filePath}`, 'FS_NOT_FOUND', { path: filePath, operation: 'read' });
@@ -259,7 +263,7 @@ function readFileContent(filePath, encoding = 'utf-8') {
259
263
  * ```
260
264
  */
261
265
  function readFileIfExists(filePath, encoding = 'utf-8') {
262
- if (!existsSync(filePath)) {
266
+ if (!isSafePath(filePath) || !existsSync(filePath)) {
263
267
  return null;
264
268
  }
265
269
  try {
@@ -9,6 +9,7 @@ const index_cjs_js$2 = require('../../_dependencies/@hyperfrontend/immutable-api
9
9
  const index_cjs_js = require('../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/set/index.cjs.js');
10
10
  const index_cjs_js$1 = require('../../_dependencies/@hyperfrontend/logging/index.cjs.js');
11
11
  const index_cjs_js$6 = require('../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/error/index.cjs.js');
12
+ const { isSafePath } = require('../../_shared/core/fs/guard/index.cjs.js');
12
13
  const { getFileStat, isFile, isDirectory, isSymlink, exists } = require('../../_shared/core/fs/stat/index.cjs.js');
13
14
  const { join } = require('../../_shared/core/path/join/index.cjs.js');
14
15
 
@@ -197,6 +198,9 @@ function createFileSystemError(message, code, context) {
197
198
  * ```
198
199
  */
199
200
  function readFileContent(filePath, encoding = 'utf-8') {
201
+ if (!isSafePath(filePath)) {
202
+ throw createFileSystemError(`Unsafe file path: ${filePath}`, 'FS_READ_ERROR', { path: filePath, operation: 'read' });
203
+ }
200
204
  if (!node_fs.existsSync(filePath)) {
201
205
  fsLogger.debug('File not found', { path: filePath });
202
206
  throw createFileSystemError(`File not found: ${filePath}`, 'FS_NOT_FOUND', { path: filePath, operation: 'read' });
@@ -223,6 +227,9 @@ function readFileContent(filePath, encoding = 'utf-8') {
223
227
  * ```
224
228
  */
225
229
  function readFileBuffer(filePath) {
230
+ if (!isSafePath(filePath)) {
231
+ throw createFileSystemError(`Unsafe file path: ${filePath}`, 'FS_READ_ERROR', { path: filePath, operation: 'read' });
232
+ }
226
233
  if (!node_fs.existsSync(filePath)) {
227
234
  throw createFileSystemError(`File not found: ${filePath}`, 'FS_NOT_FOUND', { path: filePath, operation: 'read' });
228
235
  }
@@ -249,7 +256,7 @@ function readFileBuffer(filePath) {
249
256
  * ```
250
257
  */
251
258
  function readFileIfExists(filePath, encoding = 'utf-8') {
252
- if (!node_fs.existsSync(filePath)) {
259
+ if (!isSafePath(filePath) || !node_fs.existsSync(filePath)) {
253
260
  return null;
254
261
  }
255
262
  try {
@@ -281,6 +288,9 @@ function readFileIfExists(filePath, encoding = 'utf-8') {
281
288
  * ```
282
289
  */
283
290
  function readJsonFile(filePath, options) {
291
+ if (!isSafePath(filePath)) {
292
+ throw createFileSystemError(`Unsafe file path: ${filePath}`, 'FS_READ_ERROR', { path: filePath, operation: 'read' });
293
+ }
284
294
  if (!node_fs.existsSync(filePath)) {
285
295
  if (options && 'default' in options) {
286
296
  fsLogger.debug('JSON file not found, using default', { path: filePath });
@@ -320,7 +330,7 @@ function readJsonFile(filePath, options) {
320
330
  * ```
321
331
  */
322
332
  function readJsonFileIfExists(filePath) {
323
- if (!node_fs.existsSync(filePath)) {
333
+ if (!isSafePath(filePath) || !node_fs.existsSync(filePath)) {
324
334
  return null;
325
335
  }
326
336
  try {
@@ -7,6 +7,7 @@ import { freeze, entries, keys, defineProperties } from '../../_dependencies/@hy
7
7
  import { createSet } from '../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/set/index.esm.js';
8
8
  import { createLogger } from '../../_dependencies/@hyperfrontend/logging/index.esm.js';
9
9
  import { createError } from '../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/error/index.esm.js';
10
+ import { isSafePath } from '../../_shared/core/fs/guard/index.esm.js';
10
11
  import { getFileStat, isFile, isDirectory, isSymlink, exists } from '../../_shared/core/fs/stat/index.esm.js';
11
12
  import { join } from '../../_shared/core/path/join/index.esm.js';
12
13
 
@@ -195,6 +196,9 @@ function createFileSystemError(message, code, context) {
195
196
  * ```
196
197
  */
197
198
  function readFileContent(filePath, encoding = 'utf-8') {
199
+ if (!isSafePath(filePath)) {
200
+ throw createFileSystemError(`Unsafe file path: ${filePath}`, 'FS_READ_ERROR', { path: filePath, operation: 'read' });
201
+ }
198
202
  if (!existsSync(filePath)) {
199
203
  fsLogger.debug('File not found', { path: filePath });
200
204
  throw createFileSystemError(`File not found: ${filePath}`, 'FS_NOT_FOUND', { path: filePath, operation: 'read' });
@@ -221,6 +225,9 @@ function readFileContent(filePath, encoding = 'utf-8') {
221
225
  * ```
222
226
  */
223
227
  function readFileBuffer(filePath) {
228
+ if (!isSafePath(filePath)) {
229
+ throw createFileSystemError(`Unsafe file path: ${filePath}`, 'FS_READ_ERROR', { path: filePath, operation: 'read' });
230
+ }
224
231
  if (!existsSync(filePath)) {
225
232
  throw createFileSystemError(`File not found: ${filePath}`, 'FS_NOT_FOUND', { path: filePath, operation: 'read' });
226
233
  }
@@ -247,7 +254,7 @@ function readFileBuffer(filePath) {
247
254
  * ```
248
255
  */
249
256
  function readFileIfExists(filePath, encoding = 'utf-8') {
250
- if (!existsSync(filePath)) {
257
+ if (!isSafePath(filePath) || !existsSync(filePath)) {
251
258
  return null;
252
259
  }
253
260
  try {
@@ -279,6 +286,9 @@ function readFileIfExists(filePath, encoding = 'utf-8') {
279
286
  * ```
280
287
  */
281
288
  function readJsonFile(filePath, options) {
289
+ if (!isSafePath(filePath)) {
290
+ throw createFileSystemError(`Unsafe file path: ${filePath}`, 'FS_READ_ERROR', { path: filePath, operation: 'read' });
291
+ }
282
292
  if (!existsSync(filePath)) {
283
293
  if (options && 'default' in options) {
284
294
  fsLogger.debug('JSON file not found, using default', { path: filePath });
@@ -318,7 +328,7 @@ function readJsonFile(filePath, options) {
318
328
  * ```
319
329
  */
320
330
  function readJsonFileIfExists(filePath) {
321
- if (!existsSync(filePath)) {
331
+ if (!isSafePath(filePath) || !existsSync(filePath)) {
322
332
  return null;
323
333
  }
324
334
  try {
package/core/index.cjs.js CHANGED
@@ -12,9 +12,10 @@ const index_cjs_js$7 = require('../_dependencies/@hyperfrontend/immutable-api-ut
12
12
  const node_fs = require('node:fs');
13
13
  const node_path = require('node:path');
14
14
  const node_os = require('node:os');
15
+ const { isSafePath } = require('../_shared/core/fs/guard/index.cjs.js');
15
16
  const { getFileStat, isFile, isDirectory, isSymlink, exists } = require('../_shared/core/fs/stat/index.cjs.js');
16
- const { join, joinPosix } = require('../_shared/core/path/join/index.cjs.js');
17
17
  const { normalizePath, normalizeToForwardSlashes, normalizeToNative, removeTrailingSlash, ensureTrailingSlash } = require('../_shared/core/path/normalize/index.cjs.js');
18
+ const { join, joinPosix } = require('../_shared/core/path/join/index.cjs.js');
18
19
  const { resolvePath, resolveFromWorkspace, resolveRealPath, relativePath, joinPath, isAbsolute, offsetFromRoot } = require('../_shared/core/path/resolve/index.cjs.js');
19
20
  const { pathSegments, getBasename, getDirname, getExtension, getFileNameWithoutExtension, parsePath } = require('../_shared/core/path/segments/index.cjs.js');
20
21
  const { createStructuredError, createConfigError, createFsError, createParseError, createValidationError } = require('../_shared/core/errors/structured-errors/index.cjs.js');
@@ -512,6 +513,9 @@ function createFileSystemError(message, code, context) {
512
513
  * ```
513
514
  */
514
515
  function readFileContent(filePath, encoding = 'utf-8') {
516
+ if (!isSafePath(filePath)) {
517
+ throw createFileSystemError(`Unsafe file path: ${filePath}`, 'FS_READ_ERROR', { path: filePath, operation: 'read' });
518
+ }
515
519
  if (!node_fs.existsSync(filePath)) {
516
520
  fsLogger.debug('File not found', { path: filePath });
517
521
  throw createFileSystemError(`File not found: ${filePath}`, 'FS_NOT_FOUND', { path: filePath, operation: 'read' });
@@ -538,6 +542,9 @@ function readFileContent(filePath, encoding = 'utf-8') {
538
542
  * ```
539
543
  */
540
544
  function readFileBuffer(filePath) {
545
+ if (!isSafePath(filePath)) {
546
+ throw createFileSystemError(`Unsafe file path: ${filePath}`, 'FS_READ_ERROR', { path: filePath, operation: 'read' });
547
+ }
541
548
  if (!node_fs.existsSync(filePath)) {
542
549
  throw createFileSystemError(`File not found: ${filePath}`, 'FS_NOT_FOUND', { path: filePath, operation: 'read' });
543
550
  }
@@ -564,7 +571,7 @@ function readFileBuffer(filePath) {
564
571
  * ```
565
572
  */
566
573
  function readFileIfExists(filePath, encoding = 'utf-8') {
567
- if (!node_fs.existsSync(filePath)) {
574
+ if (!isSafePath(filePath) || !node_fs.existsSync(filePath)) {
568
575
  return null;
569
576
  }
570
577
  try {
@@ -596,6 +603,9 @@ function readFileIfExists(filePath, encoding = 'utf-8') {
596
603
  * ```
597
604
  */
598
605
  function readJsonFile(filePath, options) {
606
+ if (!isSafePath(filePath)) {
607
+ throw createFileSystemError(`Unsafe file path: ${filePath}`, 'FS_READ_ERROR', { path: filePath, operation: 'read' });
608
+ }
599
609
  if (!node_fs.existsSync(filePath)) {
600
610
  if (options && 'default' in options) {
601
611
  fsLogger.debug('JSON file not found, using default', { path: filePath });
@@ -635,7 +645,7 @@ function readJsonFile(filePath, options) {
635
645
  * ```
636
646
  */
637
647
  function readJsonFileIfExists(filePath) {
638
- if (!node_fs.existsSync(filePath)) {
648
+ if (!isSafePath(filePath) || !node_fs.existsSync(filePath)) {
639
649
  return null;
640
650
  }
641
651
  try {
package/core/index.esm.js CHANGED
@@ -8,11 +8,12 @@ import { stringify, parse } from '../_dependencies/@hyperfrontend/immutable-api-
8
8
  import { createLogger } from '../_dependencies/@hyperfrontend/logging/index.esm.js';
9
9
  import { min } from '../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/math/index.esm.js';
10
10
  import { existsSync, readFileSync, statSync, lstatSync, mkdirSync, readdirSync, rmSync, realpathSync, writeFileSync, mkdtempSync, unlinkSync, rmdirSync } from 'node:fs';
11
- import { join as join$1, posix, normalize, sep, isAbsolute as isAbsolute$1, relative, resolve, basename, dirname, extname, parse as parse$1 } from 'node:path';
11
+ import { join as join$1, normalize, sep, posix, isAbsolute as isAbsolute$1, relative, resolve, basename, dirname, extname, parse as parse$1 } from 'node:path';
12
12
  import { tmpdir, platform, arch } from 'node:os';
13
+ import { isSafePath } from '../_shared/core/fs/guard/index.esm.js';
13
14
  import { getFileStat, isFile, isDirectory, isSymlink, exists } from '../_shared/core/fs/stat/index.esm.js';
14
- import { join, joinPosix } from '../_shared/core/path/join/index.esm.js';
15
15
  import { normalizePath, normalizeToForwardSlashes, normalizeToNative, removeTrailingSlash, ensureTrailingSlash } from '../_shared/core/path/normalize/index.esm.js';
16
+ import { join, joinPosix } from '../_shared/core/path/join/index.esm.js';
16
17
  import { resolvePath, resolveFromWorkspace, resolveRealPath, relativePath, joinPath, isAbsolute, offsetFromRoot } from '../_shared/core/path/resolve/index.esm.js';
17
18
  import { pathSegments, getBasename, getDirname, getExtension, getFileNameWithoutExtension, parsePath } from '../_shared/core/path/segments/index.esm.js';
18
19
  import { createStructuredError, createConfigError, createFsError, createParseError, createValidationError } from '../_shared/core/errors/structured-errors/index.esm.js';
@@ -510,6 +511,9 @@ function createFileSystemError(message, code, context) {
510
511
  * ```
511
512
  */
512
513
  function readFileContent(filePath, encoding = 'utf-8') {
514
+ if (!isSafePath(filePath)) {
515
+ throw createFileSystemError(`Unsafe file path: ${filePath}`, 'FS_READ_ERROR', { path: filePath, operation: 'read' });
516
+ }
513
517
  if (!existsSync(filePath)) {
514
518
  fsLogger.debug('File not found', { path: filePath });
515
519
  throw createFileSystemError(`File not found: ${filePath}`, 'FS_NOT_FOUND', { path: filePath, operation: 'read' });
@@ -536,6 +540,9 @@ function readFileContent(filePath, encoding = 'utf-8') {
536
540
  * ```
537
541
  */
538
542
  function readFileBuffer(filePath) {
543
+ if (!isSafePath(filePath)) {
544
+ throw createFileSystemError(`Unsafe file path: ${filePath}`, 'FS_READ_ERROR', { path: filePath, operation: 'read' });
545
+ }
539
546
  if (!existsSync(filePath)) {
540
547
  throw createFileSystemError(`File not found: ${filePath}`, 'FS_NOT_FOUND', { path: filePath, operation: 'read' });
541
548
  }
@@ -562,7 +569,7 @@ function readFileBuffer(filePath) {
562
569
  * ```
563
570
  */
564
571
  function readFileIfExists(filePath, encoding = 'utf-8') {
565
- if (!existsSync(filePath)) {
572
+ if (!isSafePath(filePath) || !existsSync(filePath)) {
566
573
  return null;
567
574
  }
568
575
  try {
@@ -594,6 +601,9 @@ function readFileIfExists(filePath, encoding = 'utf-8') {
594
601
  * ```
595
602
  */
596
603
  function readJsonFile(filePath, options) {
604
+ if (!isSafePath(filePath)) {
605
+ throw createFileSystemError(`Unsafe file path: ${filePath}`, 'FS_READ_ERROR', { path: filePath, operation: 'read' });
606
+ }
597
607
  if (!existsSync(filePath)) {
598
608
  if (options && 'default' in options) {
599
609
  fsLogger.debug('JSON file not found, using default', { path: filePath });
@@ -633,7 +643,7 @@ function readJsonFile(filePath, options) {
633
643
  * ```
634
644
  */
635
645
  function readJsonFileIfExists(filePath) {
636
- if (!existsSync(filePath)) {
646
+ if (!isSafePath(filePath) || !existsSync(filePath)) {
637
647
  return null;
638
648
  }
639
649
  try {
@@ -1,9 +1,10 @@
1
1
  'use strict';
2
2
 
3
- const node_path = require('node:path');
4
3
  const node_fs = require('node:fs');
5
- const { join, joinPosix } = require('../../_shared/core/path/join/index.cjs.js');
4
+ const node_path = require('node:path');
6
5
  const { normalizePath, normalizeToForwardSlashes, normalizeToNative, removeTrailingSlash, ensureTrailingSlash } = require('../../_shared/core/path/normalize/index.cjs.js');
6
+ const { isWithinRoot } = require('../../_shared/core/path/confine/index.cjs.js');
7
+ const { join, joinPosix } = require('../../_shared/core/path/join/index.cjs.js');
7
8
  const { resolvePath, resolveFromWorkspace, resolveRealPath, relativePath, joinPath, isAbsolute, offsetFromRoot } = require('../../_shared/core/path/resolve/index.cjs.js');
8
9
  const { pathSegments, getBasename, getDirname, getExtension, getFileNameWithoutExtension, parsePath } = require('../../_shared/core/path/segments/index.cjs.js');
9
10
 
@@ -13,6 +14,7 @@ exports.getDirname = getDirname;
13
14
  exports.getExtension = getExtension;
14
15
  exports.getFileNameWithoutExtension = getFileNameWithoutExtension;
15
16
  exports.isAbsolute = isAbsolute;
17
+ exports.isWithinRoot = isWithinRoot;
16
18
  exports.join = join;
17
19
  exports.joinPath = joinPath;
18
20
  exports.joinPosix = joinPosix;
@@ -99,6 +99,27 @@ declare function getFileNameWithoutExtension(filePath: string): string;
99
99
  */
100
100
  declare function parsePath(filePath: string): ParsedPath;
101
101
 
102
+ /**
103
+ * Reports whether `candidate` resolves to a location inside `root`.
104
+ *
105
+ * The lexical check runs first and rejects `..` traversal and absolute escapes
106
+ * with no filesystem access at all, so a hostile path is never stat-ed. Only a
107
+ * path that is already lexically contained is then resolved through symlinks, so
108
+ * an in-tree link cannot tunnel outside the root — and that realpath touches
109
+ * only paths the caller would legitimately read anyway.
110
+ *
111
+ * @param root - The directory the candidate must stay within.
112
+ * @param candidate - The path to validate (resolved relative to the cwd if not absolute).
113
+ * @returns True when the candidate is the root or contained within it.
114
+ *
115
+ * @example Confining a resolved import target
116
+ * ```typescript
117
+ * isWithinRoot('/project', '/project/src/index.ts') // true
118
+ * isWithinRoot('/project', '/project/../etc/passwd') // false
119
+ * ```
120
+ */
121
+ declare function isWithinRoot(root: string, candidate: string): boolean;
122
+
102
123
  /**
103
124
  * Join path segments.
104
125
  * Uses platform-specific separators (e.g., / or \).
@@ -302,5 +323,5 @@ declare function isAbsolute(filePath: string): boolean;
302
323
  */
303
324
  declare function offsetFromRoot(filePath: string): string;
304
325
 
305
- export { ensureTrailingSlash, getBasename, getDirname, getExtension, getFileNameWithoutExtension, isAbsolute, join, joinPath, joinPosix, normalizePath, normalizeToForwardSlashes, normalizeToNative, offsetFromRoot, parsePath, pathSegments, relativePath, removeTrailingSlash, resolveFromWorkspace, resolvePath, resolveRealPath };
326
+ export { ensureTrailingSlash, getBasename, getDirname, getExtension, getFileNameWithoutExtension, isAbsolute, isWithinRoot, join, joinPath, joinPosix, normalizePath, normalizeToForwardSlashes, normalizeToNative, offsetFromRoot, parsePath, pathSegments, relativePath, removeTrailingSlash, resolveFromWorkspace, resolvePath, resolveRealPath };
306
327
  export type { ParsedPath };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../libs/project-scope/src/core/path/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AACxC,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,yBAAyB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AACnI,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,oBAAoB,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAClI,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,2BAA2B,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../libs/project-scope/src/core/path/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AACxC,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,yBAAyB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AACnI,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,oBAAoB,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAClI,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,2BAA2B,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA"}
@@ -1,8 +1,9 @@
1
- import { join as join$1, posix, normalize, sep, isAbsolute as isAbsolute$1, relative, resolve, basename, dirname, extname, parse } from 'node:path';
2
1
  import { existsSync, realpathSync } from 'node:fs';
3
- import { join, joinPosix } from '../../_shared/core/path/join/index.esm.js';
2
+ import { normalize, sep, resolve, join as join$1, posix, isAbsolute as isAbsolute$1, relative, basename, dirname, extname, parse } from 'node:path';
4
3
  import { normalizePath, normalizeToForwardSlashes, normalizeToNative, removeTrailingSlash, ensureTrailingSlash } from '../../_shared/core/path/normalize/index.esm.js';
4
+ import { isWithinRoot } from '../../_shared/core/path/confine/index.esm.js';
5
+ import { join, joinPosix } from '../../_shared/core/path/join/index.esm.js';
5
6
  import { resolvePath, resolveFromWorkspace, resolveRealPath, relativePath, joinPath, isAbsolute, offsetFromRoot } from '../../_shared/core/path/resolve/index.esm.js';
6
7
  import { pathSegments, getBasename, getDirname, getExtension, getFileNameWithoutExtension, parsePath } from '../../_shared/core/path/segments/index.esm.js';
7
8
 
8
- export { ensureTrailingSlash, getBasename, getDirname, getExtension, getFileNameWithoutExtension, isAbsolute, join, joinPath, joinPosix, normalizePath, normalizeToForwardSlashes, normalizeToNative, offsetFromRoot, parsePath, pathSegments, relativePath, removeTrailingSlash, resolveFromWorkspace, resolvePath, resolveRealPath };
9
+ export { ensureTrailingSlash, getBasename, getDirname, getExtension, getFileNameWithoutExtension, isAbsolute, isWithinRoot, join, joinPath, joinPosix, normalizePath, normalizeToForwardSlashes, normalizeToNative, offsetFromRoot, parsePath, pathSegments, relativePath, removeTrailingSlash, resolveFromWorkspace, resolvePath, resolveRealPath };
@@ -10,7 +10,9 @@ const index_cjs_js$3 = require('../../_dependencies/@hyperfrontend/immutable-api
10
10
  const index_cjs_js$5 = require('../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/json/index.cjs.js');
11
11
  const index_cjs_js$1 = require('../../_dependencies/@hyperfrontend/logging/index.cjs.js');
12
12
  const index_cjs_js$6 = require('../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/error/index.cjs.js');
13
+ const { isSafePath } = require('../../_shared/core/fs/guard/index.cjs.js');
13
14
  const { isFile, isDirectory, exists } = require('../../_shared/core/fs/stat/index.cjs.js');
15
+ const { isWithinRoot } = require('../../_shared/core/path/confine/index.cjs.js');
14
16
 
15
17
  /**
16
18
  * Global log level registry.
@@ -197,6 +199,9 @@ function createFileSystemError(message, code, context) {
197
199
  * ```
198
200
  */
199
201
  function readFileContent(filePath, encoding = 'utf-8') {
202
+ if (!isSafePath(filePath)) {
203
+ throw createFileSystemError(`Unsafe file path: ${filePath}`, 'FS_READ_ERROR', { path: filePath, operation: 'read' });
204
+ }
200
205
  if (!node_fs.existsSync(filePath)) {
201
206
  fsLogger.debug('File not found', { path: filePath });
202
207
  throw createFileSystemError(`File not found: ${filePath}`, 'FS_NOT_FOUND', { path: filePath, operation: 'read' });
@@ -225,7 +230,7 @@ function readFileContent(filePath, encoding = 'utf-8') {
225
230
  * ```
226
231
  */
227
232
  function readFileIfExists(filePath, encoding = 'utf-8') {
228
- if (!node_fs.existsSync(filePath)) {
233
+ if (!isSafePath(filePath) || !node_fs.existsSync(filePath)) {
229
234
  return null;
230
235
  }
231
236
  try {
@@ -434,6 +439,10 @@ function resolveImportPath(importPath, fromFile, projectPath, extensions) {
434
439
  }
435
440
  const fromDir = node_path.dirname(fromFile);
436
441
  const absolutePath = node_path.resolve(fromDir, importPath);
442
+ // why: An import string comes from untrusted source code; reject targets that escape the project root before any stat, so a hostile `../../etc/passwd` can't probe the filesystem.
443
+ if (!isWithinRoot(projectPath, absolutePath)) {
444
+ return null;
445
+ }
437
446
  if (exists(absolutePath)) {
438
447
  if (isFile(absolutePath)) {
439
448
  return node_path.relative(projectPath, absolutePath);
@@ -1,14 +1,16 @@
1
- import { join, relative, dirname, resolve } from 'node:path';
1
+ import { join, normalize, sep, resolve, relative, dirname } from 'node:path';
2
2
  import { createMap } from '../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/map/index.esm.js';
3
3
  import { freeze, entries, keys, defineProperties, values } from '../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/object/index.esm.js';
4
4
  import { createSet } from '../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/set/index.esm.js';
5
- import { existsSync, readFileSync, statSync, lstatSync, readdirSync } from 'node:fs';
5
+ import { existsSync, readFileSync, statSync, lstatSync, readdirSync, realpathSync } from 'node:fs';
6
6
  import { isArray } from '../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/array/index.esm.js';
7
7
  import { error, warn, log, info, debug } from '../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/console/index.esm.js';
8
8
  import { stringify, parse } from '../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/json/index.esm.js';
9
9
  import { createLogger } from '../../_dependencies/@hyperfrontend/logging/index.esm.js';
10
10
  import { createError } from '../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/error/index.esm.js';
11
+ import { isSafePath } from '../../_shared/core/fs/guard/index.esm.js';
11
12
  import { isFile, isDirectory, exists } from '../../_shared/core/fs/stat/index.esm.js';
13
+ import { isWithinRoot } from '../../_shared/core/path/confine/index.esm.js';
12
14
 
13
15
  /**
14
16
  * Global log level registry.
@@ -195,6 +197,9 @@ function createFileSystemError(message, code, context) {
195
197
  * ```
196
198
  */
197
199
  function readFileContent(filePath, encoding = 'utf-8') {
200
+ if (!isSafePath(filePath)) {
201
+ throw createFileSystemError(`Unsafe file path: ${filePath}`, 'FS_READ_ERROR', { path: filePath, operation: 'read' });
202
+ }
198
203
  if (!existsSync(filePath)) {
199
204
  fsLogger.debug('File not found', { path: filePath });
200
205
  throw createFileSystemError(`File not found: ${filePath}`, 'FS_NOT_FOUND', { path: filePath, operation: 'read' });
@@ -223,7 +228,7 @@ function readFileContent(filePath, encoding = 'utf-8') {
223
228
  * ```
224
229
  */
225
230
  function readFileIfExists(filePath, encoding = 'utf-8') {
226
- if (!existsSync(filePath)) {
231
+ if (!isSafePath(filePath) || !existsSync(filePath)) {
227
232
  return null;
228
233
  }
229
234
  try {
@@ -432,6 +437,10 @@ function resolveImportPath(importPath, fromFile, projectPath, extensions) {
432
437
  }
433
438
  const fromDir = dirname(fromFile);
434
439
  const absolutePath = resolve(fromDir, importPath);
440
+ // why: An import string comes from untrusted source code; reject targets that escape the project root before any stat, so a hostile `../../etc/passwd` can't probe the filesystem.
441
+ if (!isWithinRoot(projectPath, absolutePath)) {
442
+ return null;
443
+ }
435
444
  if (exists(absolutePath)) {
436
445
  if (isFile(absolutePath)) {
437
446
  return relative(projectPath, absolutePath);
@@ -10,6 +10,7 @@ const index_cjs_js$4 = require('../../_dependencies/@hyperfrontend/immutable-api
10
10
  const index_cjs_js$6 = require('../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/json/index.cjs.js');
11
11
  const index_cjs_js$3 = require('../../_dependencies/@hyperfrontend/logging/index.cjs.js');
12
12
  const index_cjs_js$7 = require('../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/error/index.cjs.js');
13
+ const { isSafePath } = require('../../_shared/core/fs/guard/index.cjs.js');
13
14
  const { isDirectory, exists } = require('../../_shared/core/fs/stat/index.cjs.js');
14
15
  const { createCache } = require('../../_shared/core/cache/index.cjs.js');
15
16
  const { matchGlobPattern } = require('../../_shared/core/patterns/glob/index.cjs.js');
@@ -198,7 +199,7 @@ function createFileSystemError(message, code, context) {
198
199
  * ```
199
200
  */
200
201
  function readFileIfExists(filePath, encoding = 'utf-8') {
201
- if (!node_fs.existsSync(filePath)) {
202
+ if (!isSafePath(filePath) || !node_fs.existsSync(filePath)) {
202
203
  return null;
203
204
  }
204
205
  try {
@@ -8,6 +8,7 @@ import { error, warn, log, info, debug } from '../../_dependencies/@hyperfronten
8
8
  import { stringify, parse } from '../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/json/index.esm.js';
9
9
  import { createLogger } from '../../_dependencies/@hyperfrontend/logging/index.esm.js';
10
10
  import { createError } from '../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/error/index.esm.js';
11
+ import { isSafePath } from '../../_shared/core/fs/guard/index.esm.js';
11
12
  import { isDirectory, exists } from '../../_shared/core/fs/stat/index.esm.js';
12
13
  import { createCache } from '../../_shared/core/cache/index.esm.js';
13
14
  import { matchGlobPattern } from '../../_shared/core/patterns/glob/index.esm.js';
@@ -196,7 +197,7 @@ function createFileSystemError(message, code, context) {
196
197
  * ```
197
198
  */
198
199
  function readFileIfExists(filePath, encoding = 'utf-8') {
199
- if (!existsSync(filePath)) {
200
+ if (!isSafePath(filePath) || !existsSync(filePath)) {
200
201
  return null;
201
202
  }
202
203
  try {
@@ -11,6 +11,7 @@ const node_path = require('node:path');
11
11
  const index_cjs_js$7 = require('../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/error/index.cjs.js');
12
12
  const node_fs = require('node:fs');
13
13
  const index_cjs_js$8 = require('../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/math/index.cjs.js');
14
+ const { isSafePath } = require('../../_shared/core/fs/guard/index.cjs.js');
14
15
  const { isDirectory, exists } = require('../../_shared/core/fs/stat/index.cjs.js');
15
16
  const { join } = require('../../_shared/core/path/join/index.cjs.js');
16
17
  const { createCache } = require('../../_shared/core/cache/index.cjs.js');
@@ -201,7 +202,7 @@ function createFileSystemError(message, code, context) {
201
202
  * ```
202
203
  */
203
204
  function readFileIfExists(filePath, encoding = 'utf-8') {
204
- if (!node_fs.existsSync(filePath)) {
205
+ if (!isSafePath(filePath) || !node_fs.existsSync(filePath)) {
205
206
  return null;
206
207
  }
207
208
  try {