file-entry-cache 11.0.0-beta.1 → 11.0.0-beta.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.
- package/README.md +218 -51
- package/dist/index.cjs +86 -27
- package/dist/index.d.cts +75 -17
- package/dist/index.d.ts +75 -17
- package/dist/index.js +86 -27
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
- Ideal for processes that work on a specific set of files
|
|
16
16
|
- Persists cache to Disk via `reconcile()` or `persistInterval` on `cache` options.
|
|
17
17
|
- Uses `checksum` to determine if a file has changed
|
|
18
|
-
- Supports `relative` and `absolute` paths
|
|
18
|
+
- Supports `relative` and `absolute` paths with configurable current working directory
|
|
19
19
|
- Portable cache files when using relative paths
|
|
20
20
|
- ESM and CommonJS support with Typescript
|
|
21
21
|
|
|
@@ -23,11 +23,17 @@
|
|
|
23
23
|
|
|
24
24
|
- [Installation](#installation)
|
|
25
25
|
- [Getting Started](#getting-started)
|
|
26
|
+
- [Changes from v10 to v11](#changes-from-v10-to-v11)
|
|
26
27
|
- [Changes from v9 to v10](#changes-from-v9-to-v10)
|
|
27
28
|
- [Global Default Functions](#global-default-functions)
|
|
28
29
|
- [FileEntryCache Options (FileEntryCacheOptions)](#fileentrycache-options-fileentrycacheoptions)
|
|
29
30
|
- [API](#api)
|
|
30
31
|
- [Get File Descriptor](#get-file-descriptor)
|
|
32
|
+
- [Path Handling and Current Working Directory](#path-handling-and-current-working-directory)
|
|
33
|
+
- [Cache Portability](#cache-portability)
|
|
34
|
+
- [Maximum Portability with Checksums](#maximum-portability-with-checksums)
|
|
35
|
+
- [Handling Project Relocations](#handling-project-relocations)
|
|
36
|
+
- [Path Security and Traversal Prevention](#path-security-and-traversal-prevention)
|
|
31
37
|
- [Using Checksums to Determine if a File has Changed (useCheckSum)](#using-checksums-to-determine-if-a-file-has-changed-usechecksum)
|
|
32
38
|
- [Setting Additional Meta Data](#setting-additional-meta-data)
|
|
33
39
|
- [How to Contribute](#how-to-contribute)
|
|
@@ -78,41 +84,16 @@ let fileDescriptor = cache.getFileDescriptor('./src/file.txt');
|
|
|
78
84
|
console.log(fileDescriptor.changed); // false as it has not changed from the saved cache.
|
|
79
85
|
```
|
|
80
86
|
|
|
81
|
-
## Migration Guide from v10 to v11
|
|
82
|
-
|
|
83
|
-
The main breaking change is the removal of `currentWorkingDirectory`. Here's how to update your code:
|
|
84
|
-
|
|
85
|
-
**Before (v10):**
|
|
86
|
-
```javascript
|
|
87
|
-
const cache = new FileEntryCache({
|
|
88
|
-
currentWorkingDirectory: '/project/root'
|
|
89
|
-
});
|
|
90
|
-
// This would store the key as 'src/file.js'
|
|
91
|
-
cache.getFileDescriptor('/project/root/src/file.js');
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
**After (v11):**
|
|
95
|
-
```javascript
|
|
96
|
-
const cache = new FileEntryCache();
|
|
97
|
-
// Now stores the key exactly as provided
|
|
98
|
-
cache.getFileDescriptor('./src/file.js'); // Key: './src/file.js'
|
|
99
|
-
cache.getFileDescriptor('/project/root/src/file.js'); // Key: '/project/root/src/file.js'
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
If you were using absolute paths with `currentWorkingDirectory`, you'll need to update your code to use relative paths if you want portable cache files.
|
|
103
87
|
|
|
104
88
|
# Changes from v10 to v11
|
|
105
89
|
|
|
106
90
|
**BREAKING CHANGES:**
|
|
107
|
-
-
|
|
108
|
-
- **Path handling changes** - The cache now stores paths exactly as they are provided:
|
|
109
|
-
- Relative paths remain relative in the cache
|
|
110
|
-
- Absolute paths remain absolute in the cache
|
|
111
|
-
- The same file accessed with different path formats will create separate cache entries
|
|
112
|
-
- **Renamed method** - `renameAbsolutePathKeys()` is now `renameCacheKeys()` to reflect that it works with any path format
|
|
113
|
-
- **Simplified API** - Removed `currentWorkingDirectory` parameter from all methods including `getFileDescriptor()`, `removeEntry()`, and factory functions
|
|
91
|
+
- **`strictPaths` now defaults to `true`** - Path traversal protection is enabled by default for security. To restore v10 behavior, explicitly set `strictPaths: false`
|
|
114
92
|
|
|
115
|
-
|
|
93
|
+
**NEW FEATURES:**
|
|
94
|
+
- **Added `cwd` option** - You can now specify a custom current working directory for resolving relative paths
|
|
95
|
+
- **Added `strictPaths` option** - Provides protection against path traversal attacks (enabled by default)
|
|
96
|
+
- **Improved cache portability** - When using relative paths with the same `cwd`, cache files are portable across different environments
|
|
116
97
|
|
|
117
98
|
# Changes from v9 to v10
|
|
118
99
|
|
|
@@ -127,13 +108,15 @@ There have been many features added and changes made to the `file-entry-cache` c
|
|
|
127
108
|
- On `FileEntryDescriptor.meta` if using typescript you need to use the `meta.data` to set additional information. This is to allow for better type checking and to avoid conflicts with the `meta` object which was `any`.
|
|
128
109
|
|
|
129
110
|
# Global Default Functions
|
|
130
|
-
- `create(cacheId: string, cacheDirectory?: string, useCheckSum?: boolean)` - Creates a new instance of the `FileEntryCache` class
|
|
131
|
-
- `createFromFile(cachePath: string, useCheckSum?: boolean)` - Creates a new instance of the `FileEntryCache` class and loads the cache from a file.
|
|
111
|
+
- `create(cacheId: string, cacheDirectory?: string, useCheckSum?: boolean, cwd?: string)` - Creates a new instance of the `FileEntryCache` class
|
|
112
|
+
- `createFromFile(cachePath: string, useCheckSum?: boolean, cwd?: string)` - Creates a new instance of the `FileEntryCache` class and loads the cache from a file.
|
|
132
113
|
|
|
133
114
|
# FileEntryCache Options (FileEntryCacheOptions)
|
|
134
115
|
- `useModifiedTime?` - If `true` it will use the modified time to determine if the file has changed. Default is `true`
|
|
135
116
|
- `useCheckSum?` - If `true` it will use a checksum to determine if the file has changed. Default is `false`
|
|
136
117
|
- `hashAlgorithm?` - The algorithm to use for the checksum. Default is `md5` but can be any algorithm supported by `crypto.createHash`
|
|
118
|
+
- `cwd?` - The current working directory for resolving relative paths. Default is `process.cwd()`
|
|
119
|
+
- `strictPaths?` - If `true` restricts file access to within `cwd` boundaries, preventing path traversal attacks. Default is `true`
|
|
137
120
|
- `cache.ttl?` - The time to live for the cache in milliseconds. Default is `0` which means no expiration
|
|
138
121
|
- `cache.lruSize?` - The number of items to keep in the cache. Default is `0` which means no limit
|
|
139
122
|
- `cache.useClone?` - If `true` it will clone the data before returning it. Default is `false`
|
|
@@ -150,17 +133,21 @@ There have been many features added and changes made to the `file-entry-cache` c
|
|
|
150
133
|
- `useCheckSum: boolean` - If `true` it will use a checksum to determine if the file has changed. Default is `false`
|
|
151
134
|
- `hashAlgorithm: string` - The algorithm to use for the checksum. Default is `md5` but can be any algorithm supported by `crypto.createHash`
|
|
152
135
|
- `getHash(buffer: Buffer): string` - Gets the hash of a buffer used for checksums
|
|
136
|
+
- `cwd: string` - The current working directory for resolving relative paths. Default is `process.cwd()`
|
|
137
|
+
- `strictPaths: boolean` - If `true` restricts file access to within `cwd` boundaries. Default is `true`
|
|
153
138
|
- `createFileKey(filePath: string): string` - Returns the cache key for the file path (returns the path exactly as provided).
|
|
154
139
|
- `deleteCacheFile(): boolean` - Deletes the cache file from disk
|
|
155
140
|
- `destroy(): void` - Destroys the cache. This will clear the cache in memory. If using cache persistence it will stop the interval.
|
|
156
|
-
- `removeEntry(filePath: string): void` - Removes an entry from the cache
|
|
141
|
+
- `removeEntry(filePath: string): void` - Removes an entry from the cache.
|
|
157
142
|
- `reconcile(): void` - Saves the cache to disk and removes any files that are no longer found.
|
|
158
143
|
- `hasFileChanged(filePath: string): boolean` - Checks if the file has changed. This will return `true` if the file has changed.
|
|
159
144
|
- `getFileDescriptor(filePath: string, options?: { useModifiedTime?: boolean, useCheckSum?: boolean }): FileEntryDescriptor` - Gets the file descriptor for the file. Please refer to the entire section on `Get File Descriptor` for more information.
|
|
160
|
-
- `normalizeEntries(
|
|
145
|
+
- `normalizeEntries(files?: string[]): FileDescriptor[]` - Normalizes the entries. If no files are provided, it will return all cached entries.
|
|
161
146
|
- `analyzeFiles(files: string[])` will return `AnalyzedFiles` object with `changedFiles`, `notFoundFiles`, and `notChangedFiles` as FileDescriptor arrays.
|
|
162
147
|
- `getUpdatedFiles(files: string[])` will return an array of `FileEntryDescriptor` objects that have changed.
|
|
163
148
|
- `getFileDescriptorsByPath(filePath: string): FileEntryDescriptor[]` will return an array of `FileEntryDescriptor` objects that starts with the path prefix specified.
|
|
149
|
+
- `getAbsolutePath(filePath: string): string` - Resolves a relative path to absolute using the configured `cwd`. Returns absolute paths unchanged. When `strictPaths` is enabled, throws an error if the path resolves outside `cwd`.
|
|
150
|
+
- `getAbsolutePathWithCwd(filePath: string, cwd: string): string` - Resolves a relative path to absolute using a custom working directory. When `strictPaths` is enabled, throws an error if the path resolves outside the provided `cwd`.
|
|
164
151
|
|
|
165
152
|
# Get File Descriptor
|
|
166
153
|
|
|
@@ -172,32 +159,94 @@ The `getFileDescriptor(filePath: string, options?: { useCheckSum?: boolean, useM
|
|
|
172
159
|
- `meta: FileEntryMeta` - The meta data for the file. This has the following properties: `size`, `mtime`, `hash`, `data`. Note that `data` is an object that can be used to store additional information.
|
|
173
160
|
- `err` - If there was an error analyzing the file.
|
|
174
161
|
|
|
175
|
-
## Path Handling
|
|
162
|
+
## Path Handling and Current Working Directory
|
|
163
|
+
|
|
164
|
+
The cache stores paths exactly as they are provided (relative or absolute). When checking if files have changed, relative paths are resolved using the configured `cwd` (current working directory):
|
|
165
|
+
|
|
166
|
+
```javascript
|
|
167
|
+
// Default: uses process.cwd()
|
|
168
|
+
const cache1 = fileEntryCache.create('cache1');
|
|
169
|
+
|
|
170
|
+
// Custom working directory
|
|
171
|
+
const cache2 = fileEntryCache.create('cache2', './cache', false, '/project/root');
|
|
172
|
+
// Or with options object
|
|
173
|
+
const cache3 = new FileEntryCache({ cwd: '/project/root' });
|
|
174
|
+
|
|
175
|
+
// The cache key is always the provided path
|
|
176
|
+
const descriptor = cache2.getFileDescriptor('./src/file.txt');
|
|
177
|
+
console.log(descriptor.key); // './src/file.txt'
|
|
178
|
+
// But file operations resolve from: '/project/root/src/file.txt'
|
|
179
|
+
```
|
|
176
180
|
|
|
177
|
-
|
|
181
|
+
### Cache Portability
|
|
178
182
|
|
|
179
|
-
|
|
180
|
-
- **Absolute paths** remain absolute in the cache
|
|
181
|
-
- The same file accessed with different path formats creates **separate cache entries**
|
|
183
|
+
Using relative paths with a consistent `cwd` (defaults to `process.cwd()`) makes cache files portable across different machines and environments. This is especially useful for CI/CD pipelines and team development.
|
|
182
184
|
|
|
183
185
|
```javascript
|
|
184
|
-
|
|
186
|
+
// On machine A (project at /home/user/project)
|
|
187
|
+
const cacheA = fileEntryCache.create('build-cache', './cache', false, '/home/user/project');
|
|
188
|
+
cacheA.getFileDescriptor('./src/index.js'); // Resolves to /home/user/project/src/index.js
|
|
189
|
+
cacheA.reconcile();
|
|
190
|
+
|
|
191
|
+
// On machine B (project at /workspace/project)
|
|
192
|
+
const cacheB = fileEntryCache.create('build-cache', './cache', false, '/workspace/project');
|
|
193
|
+
cacheB.getFileDescriptor('./src/index.js'); // Resolves to /workspace/project/src/index.js
|
|
194
|
+
// Cache hit! File hasn't changed since machine A
|
|
195
|
+
```
|
|
185
196
|
|
|
186
|
-
|
|
187
|
-
const relativeDescriptor = fileEntryCache.getFileDescriptor('./file.txt');
|
|
188
|
-
console.log(relativeDescriptor.key); // './file.txt'
|
|
197
|
+
### Maximum Portability with Checksums
|
|
189
198
|
|
|
190
|
-
|
|
191
|
-
const absolutePath = path.resolve('./file.txt');
|
|
192
|
-
const absoluteDescriptor = fileEntryCache.getFileDescriptor(absolutePath);
|
|
193
|
-
console.log(absoluteDescriptor.key); // '/full/path/to/file.txt'
|
|
199
|
+
For maximum cache portability across different environments, use checksums (`useCheckSum: true`) along with relative paths and `cwd` which defaults to `process.cwd()`. This ensures that cache validity is determined by file content rather than modification times, which can vary across systems:
|
|
194
200
|
|
|
195
|
-
|
|
201
|
+
```javascript
|
|
202
|
+
// Development machine
|
|
203
|
+
const devCache = fileEntryCache.create(
|
|
204
|
+
'.buildcache',
|
|
205
|
+
'./cache', // cache directory
|
|
206
|
+
true // Use checksums for content-based comparison
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
// Process files using relative paths
|
|
210
|
+
const descriptor = devCache.getFileDescriptor('./src/index.js');
|
|
211
|
+
if (descriptor.changed) {
|
|
212
|
+
console.log('Building ./src/index.js...');
|
|
213
|
+
// Build process here
|
|
214
|
+
}
|
|
215
|
+
devCache.reconcile(); // Save cache
|
|
216
|
+
|
|
217
|
+
// CI/CD Pipeline or another developer's machine
|
|
218
|
+
const ciCache = fileEntryCache.create(
|
|
219
|
+
'.buildcache',
|
|
220
|
+
'./node_modules/.cache',
|
|
221
|
+
true, // Same checksum setting
|
|
222
|
+
process.cwd() // Different absolute path, same relative structure
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
// Same relative path works across environments
|
|
226
|
+
const descriptor2 = ciCache.getFileDescriptor('./src/index.js');
|
|
227
|
+
if (!descriptor2.changed) {
|
|
228
|
+
console.log('Using cached result for ./src/index.js');
|
|
229
|
+
// Skip rebuild - file content unchanged
|
|
230
|
+
}
|
|
196
231
|
```
|
|
197
232
|
|
|
198
|
-
|
|
233
|
+
### Handling Project Relocations
|
|
234
|
+
|
|
235
|
+
Cache remains valid even when projects are moved or renamed:
|
|
236
|
+
|
|
237
|
+
```javascript
|
|
238
|
+
// Original location: /projects/my-app
|
|
239
|
+
const cache1 = fileEntryCache.create('.cache', './cache', true, '/projects/my-app');
|
|
240
|
+
cache1.getFileDescriptor('./src/app.js');
|
|
241
|
+
cache1.reconcile();
|
|
242
|
+
|
|
243
|
+
// After moving project to: /archived/2024/my-app
|
|
244
|
+
const cache2 = fileEntryCache.create('.cache', './cache', true, '/archived/2024/my-app');
|
|
245
|
+
cache2.getFileDescriptor('./src/app.js'); // Still finds cached entry!
|
|
246
|
+
// Cache valid as long as relative structure unchanged
|
|
247
|
+
```
|
|
199
248
|
|
|
200
|
-
If there is an error when trying to get the file descriptor it will return
|
|
249
|
+
If there is an error when trying to get the file descriptor it will return a `notFound` and `err` property with the error.
|
|
201
250
|
|
|
202
251
|
```javascript
|
|
203
252
|
const fileEntryCache = new FileEntryCache();
|
|
@@ -211,6 +260,124 @@ if (fileDescriptor.notFound) {
|
|
|
211
260
|
}
|
|
212
261
|
```
|
|
213
262
|
|
|
263
|
+
# Path Security and Traversal Prevention
|
|
264
|
+
|
|
265
|
+
The `strictPaths` option provides security against path traversal attacks by restricting file access to within the configured `cwd` boundaries. **This is enabled by default (since v11)** to ensure secure defaults when processing untrusted input or when running in security-sensitive environments.
|
|
266
|
+
|
|
267
|
+
## Basic Usage
|
|
268
|
+
|
|
269
|
+
```javascript
|
|
270
|
+
// strictPaths is enabled by default for security
|
|
271
|
+
const cache = new FileEntryCache({
|
|
272
|
+
cwd: '/project/root'
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// This will work - file is within cwd
|
|
276
|
+
const descriptor = cache.getFileDescriptor('./src/index.js');
|
|
277
|
+
|
|
278
|
+
// This will throw an error - attempts to access parent directory
|
|
279
|
+
try {
|
|
280
|
+
cache.getFileDescriptor('../../../etc/passwd');
|
|
281
|
+
} catch (error) {
|
|
282
|
+
console.error(error); // Path traversal attempt blocked
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// To allow parent directory access (not recommended for untrusted input)
|
|
286
|
+
const unsafeCache = new FileEntryCache({
|
|
287
|
+
cwd: '/project/root',
|
|
288
|
+
strictPaths: false // Explicitly disable protection
|
|
289
|
+
});
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Security Features
|
|
293
|
+
|
|
294
|
+
When `strictPaths` is enabled:
|
|
295
|
+
- **Path Traversal Prevention**: Blocks attempts to access files outside the working directory using `../` sequences
|
|
296
|
+
- **Null Byte Protection**: Automatically removes null bytes from paths to prevent injection attacks
|
|
297
|
+
- **Path Normalization**: Cleans and normalizes paths to prevent bypass attempts
|
|
298
|
+
|
|
299
|
+
## Use Cases
|
|
300
|
+
|
|
301
|
+
### Build Tools with Untrusted Input
|
|
302
|
+
```javascript
|
|
303
|
+
// Secure build tool configuration
|
|
304
|
+
const cache = fileEntryCache.create(
|
|
305
|
+
'.buildcache',
|
|
306
|
+
'./cache',
|
|
307
|
+
true, // useCheckSum
|
|
308
|
+
process.cwd()
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
// Enable strict path checking for security
|
|
312
|
+
cache.strictPaths = true;
|
|
313
|
+
|
|
314
|
+
// Process user-provided file paths safely
|
|
315
|
+
function processUserFile(userProvidedPath) {
|
|
316
|
+
try {
|
|
317
|
+
const descriptor = cache.getFileDescriptor(userProvidedPath);
|
|
318
|
+
// Safe to process - file is within boundaries
|
|
319
|
+
return descriptor;
|
|
320
|
+
} catch (error) {
|
|
321
|
+
if (error.message.includes('Path traversal attempt blocked')) {
|
|
322
|
+
console.warn('Security: Blocked access to:', userProvidedPath);
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
throw error;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### CI/CD Environments
|
|
331
|
+
```javascript
|
|
332
|
+
// Strict security for CI/CD pipelines
|
|
333
|
+
const cache = new FileEntryCache({
|
|
334
|
+
cwd: process.env.GITHUB_WORKSPACE || process.cwd(),
|
|
335
|
+
strictPaths: true, // Prevent access outside workspace
|
|
336
|
+
useCheckSum: true // Content-based validation
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
// All file operations are now restricted to the workspace
|
|
340
|
+
cache.getFileDescriptor('./src/app.js'); // ✓ Allowed
|
|
341
|
+
cache.getFileDescriptor('/etc/passwd'); // ✗ Blocked (absolute path outside cwd)
|
|
342
|
+
cache.getFileDescriptor('../../../root'); // ✗ Blocked (path traversal)
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Dynamic Security Control
|
|
346
|
+
```javascript
|
|
347
|
+
const cache = new FileEntryCache({ cwd: '/safe/directory' });
|
|
348
|
+
|
|
349
|
+
// Start with relaxed mode for trusted operations
|
|
350
|
+
cache.strictPaths = false;
|
|
351
|
+
processInternalFiles();
|
|
352
|
+
|
|
353
|
+
// Enable strict mode for untrusted input
|
|
354
|
+
cache.strictPaths = true;
|
|
355
|
+
processUserUploadedPaths();
|
|
356
|
+
|
|
357
|
+
// Return to relaxed mode if needed
|
|
358
|
+
cache.strictPaths = false;
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## Default Behavior
|
|
362
|
+
|
|
363
|
+
**As of v11, `strictPaths` is enabled by default** to provide secure defaults. This means:
|
|
364
|
+
- Path traversal attempts using `../` are blocked
|
|
365
|
+
- File access is restricted to within the configured `cwd`
|
|
366
|
+
- Null bytes in paths are automatically sanitized
|
|
367
|
+
|
|
368
|
+
### Migrating from v10 or Earlier
|
|
369
|
+
|
|
370
|
+
If you're upgrading from v10 or earlier and need to maintain the previous behavior (for example, if your code legitimately accesses parent directories), you can explicitly disable strict paths:
|
|
371
|
+
|
|
372
|
+
```javascript
|
|
373
|
+
const cache = new FileEntryCache({
|
|
374
|
+
cwd: process.cwd(),
|
|
375
|
+
strictPaths: false // Restore v10 behavior
|
|
376
|
+
});
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
However, we strongly recommend keeping `strictPaths: true` and adjusting your code to work within the security boundaries, especially when processing any untrusted input.
|
|
380
|
+
|
|
214
381
|
# Using Checksums to Determine if a File has Changed (useCheckSum)
|
|
215
382
|
|
|
216
383
|
By default the `useCheckSum` is `false`. This means that the `FileEntryCache` will use the `mtime` and `ctime` to determine if the file has changed. If you set `useCheckSum` to `true` it will use a checksum to determine if the file has changed. This is useful when you want to make sure that the file has not changed at all.
|
package/dist/index.cjs
CHANGED
|
@@ -40,14 +40,15 @@ var import_node_crypto = __toESM(require("crypto"), 1);
|
|
|
40
40
|
var import_node_fs = __toESM(require("fs"), 1);
|
|
41
41
|
var import_node_path = __toESM(require("path"), 1);
|
|
42
42
|
var import_flat_cache = require("flat-cache");
|
|
43
|
-
function createFromFile(filePath, useCheckSum) {
|
|
43
|
+
function createFromFile(filePath, useCheckSum, cwd) {
|
|
44
44
|
const fname = import_node_path.default.basename(filePath);
|
|
45
45
|
const directory = import_node_path.default.dirname(filePath);
|
|
46
|
-
return create(fname, directory, useCheckSum);
|
|
46
|
+
return create(fname, directory, useCheckSum, cwd);
|
|
47
47
|
}
|
|
48
|
-
function create(cacheId, cacheDirectory, useCheckSum) {
|
|
48
|
+
function create(cacheId, cacheDirectory, useCheckSum, cwd) {
|
|
49
49
|
const options = {
|
|
50
50
|
useCheckSum,
|
|
51
|
+
cwd,
|
|
51
52
|
cache: {
|
|
52
53
|
cacheId,
|
|
53
54
|
cacheDir: cacheDirectory
|
|
@@ -69,25 +70,29 @@ var FileEntryDefault = class {
|
|
|
69
70
|
var FileEntryCache = class {
|
|
70
71
|
_cache = new import_flat_cache.FlatCache({ useClone: false });
|
|
71
72
|
_useCheckSum = false;
|
|
72
|
-
_useModifiedTime = true;
|
|
73
73
|
_hashAlgorithm = "md5";
|
|
74
|
+
_cwd = process.cwd();
|
|
75
|
+
_strictPaths = true;
|
|
74
76
|
/**
|
|
75
77
|
* Create a new FileEntryCache instance
|
|
76
|
-
* @param options - The options for the FileEntryCache
|
|
78
|
+
* @param options - The options for the FileEntryCache (all properties are optional with defaults)
|
|
77
79
|
*/
|
|
78
80
|
constructor(options) {
|
|
79
81
|
if (options?.cache) {
|
|
80
82
|
this._cache = new import_flat_cache.FlatCache(options.cache);
|
|
81
83
|
}
|
|
82
|
-
if (options?.useModifiedTime) {
|
|
83
|
-
this._useModifiedTime = options.useModifiedTime;
|
|
84
|
-
}
|
|
85
84
|
if (options?.useCheckSum) {
|
|
86
85
|
this._useCheckSum = options.useCheckSum;
|
|
87
86
|
}
|
|
88
87
|
if (options?.hashAlgorithm) {
|
|
89
88
|
this._hashAlgorithm = options.hashAlgorithm;
|
|
90
89
|
}
|
|
90
|
+
if (options?.cwd) {
|
|
91
|
+
this._cwd = options.cwd;
|
|
92
|
+
}
|
|
93
|
+
if (options?.strictPaths !== void 0) {
|
|
94
|
+
this._strictPaths = options.strictPaths;
|
|
95
|
+
}
|
|
91
96
|
}
|
|
92
97
|
/**
|
|
93
98
|
* Get the cache
|
|
@@ -105,7 +110,7 @@ var FileEntryCache = class {
|
|
|
105
110
|
}
|
|
106
111
|
/**
|
|
107
112
|
* Use the hash to check if the file has changed
|
|
108
|
-
* @returns {boolean} if the hash is used to check if the file has changed
|
|
113
|
+
* @returns {boolean} if the hash is used to check if the file has changed (default: false)
|
|
109
114
|
*/
|
|
110
115
|
get useCheckSum() {
|
|
111
116
|
return this._useCheckSum;
|
|
@@ -117,23 +122,9 @@ var FileEntryCache = class {
|
|
|
117
122
|
set useCheckSum(value) {
|
|
118
123
|
this._useCheckSum = value;
|
|
119
124
|
}
|
|
120
|
-
/**
|
|
121
|
-
* Use the modified time to check if the file has changed
|
|
122
|
-
* @returns {boolean} if the modified time is used to check if the file has changed
|
|
123
|
-
*/
|
|
124
|
-
get useModifiedTime() {
|
|
125
|
-
return this._useModifiedTime;
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Set the useModifiedTime value
|
|
129
|
-
* @param {boolean} value - The value to set
|
|
130
|
-
*/
|
|
131
|
-
set useModifiedTime(value) {
|
|
132
|
-
this._useModifiedTime = value;
|
|
133
|
-
}
|
|
134
125
|
/**
|
|
135
126
|
* Get the hash algorithm
|
|
136
|
-
* @returns {string} The hash algorithm
|
|
127
|
+
* @returns {string} The hash algorithm (default: 'md5')
|
|
137
128
|
*/
|
|
138
129
|
get hashAlgorithm() {
|
|
139
130
|
return this._hashAlgorithm;
|
|
@@ -145,6 +136,34 @@ var FileEntryCache = class {
|
|
|
145
136
|
set hashAlgorithm(value) {
|
|
146
137
|
this._hashAlgorithm = value;
|
|
147
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* Get the current working directory
|
|
141
|
+
* @returns {string} The current working directory (default: process.cwd())
|
|
142
|
+
*/
|
|
143
|
+
get cwd() {
|
|
144
|
+
return this._cwd;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Set the current working directory
|
|
148
|
+
* @param {string} value - The value to set
|
|
149
|
+
*/
|
|
150
|
+
set cwd(value) {
|
|
151
|
+
this._cwd = value;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Get whether to restrict paths to cwd boundaries
|
|
155
|
+
* @returns {boolean} Whether strict path checking is enabled (default: true)
|
|
156
|
+
*/
|
|
157
|
+
get strictPaths() {
|
|
158
|
+
return this._strictPaths;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Set whether to restrict paths to cwd boundaries
|
|
162
|
+
* @param {boolean} value - The value to set
|
|
163
|
+
*/
|
|
164
|
+
set strictPaths(value) {
|
|
165
|
+
this._strictPaths = value;
|
|
166
|
+
}
|
|
148
167
|
/**
|
|
149
168
|
* Given a buffer, calculate md5 hash of its content.
|
|
150
169
|
* @method getHash
|
|
@@ -241,7 +260,6 @@ var FileEntryCache = class {
|
|
|
241
260
|
result.meta = this._cache.getKey(result.key) ?? {};
|
|
242
261
|
const absolutePath = this.getAbsolutePath(filePath);
|
|
243
262
|
const useCheckSumValue = options?.useCheckSum ?? this._useCheckSum;
|
|
244
|
-
const useModifiedTimeValue = options?.useModifiedTime ?? this._useModifiedTime;
|
|
245
263
|
try {
|
|
246
264
|
fstat = import_node_fs.default.statSync(absolutePath);
|
|
247
265
|
result.meta = {
|
|
@@ -274,7 +292,7 @@ var FileEntryCache = class {
|
|
|
274
292
|
if (result.meta.data === void 0) {
|
|
275
293
|
result.meta.data = metaCache.data;
|
|
276
294
|
}
|
|
277
|
-
if (
|
|
295
|
+
if (useCheckSumValue === false && metaCache?.mtime !== result.meta?.mtime) {
|
|
278
296
|
result.changed = true;
|
|
279
297
|
}
|
|
280
298
|
if (metaCache?.size !== result.meta?.size) {
|
|
@@ -369,13 +387,54 @@ var FileEntryCache = class {
|
|
|
369
387
|
}
|
|
370
388
|
/**
|
|
371
389
|
* Get the Absolute Path. If it is already absolute it will return the path as is.
|
|
390
|
+
* When strictPaths is enabled, ensures the resolved path stays within cwd boundaries.
|
|
372
391
|
* @method getAbsolutePath
|
|
373
392
|
* @param filePath - The file path to get the absolute path for
|
|
374
393
|
* @returns {string}
|
|
394
|
+
* @throws {Error} When strictPaths is true and path would resolve outside cwd
|
|
375
395
|
*/
|
|
376
396
|
getAbsolutePath(filePath) {
|
|
377
397
|
if (this.isRelativePath(filePath)) {
|
|
378
|
-
|
|
398
|
+
const sanitizedPath = filePath.replace(/\0/g, "");
|
|
399
|
+
const resolved = import_node_path.default.resolve(this._cwd, sanitizedPath);
|
|
400
|
+
if (this._strictPaths) {
|
|
401
|
+
const normalizedResolved = import_node_path.default.normalize(resolved);
|
|
402
|
+
const normalizedCwd = import_node_path.default.normalize(this._cwd);
|
|
403
|
+
const isWithinCwd = normalizedResolved === normalizedCwd || normalizedResolved.startsWith(normalizedCwd + import_node_path.default.sep);
|
|
404
|
+
if (!isWithinCwd) {
|
|
405
|
+
throw new Error(
|
|
406
|
+
`Path traversal attempt blocked: "${filePath}" resolves outside of working directory "${this._cwd}"`
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return resolved;
|
|
411
|
+
}
|
|
412
|
+
return filePath;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Get the Absolute Path with a custom working directory. If it is already absolute it will return the path as is.
|
|
416
|
+
* When strictPaths is enabled, ensures the resolved path stays within the provided cwd boundaries.
|
|
417
|
+
* @method getAbsolutePathWithCwd
|
|
418
|
+
* @param filePath - The file path to get the absolute path for
|
|
419
|
+
* @param cwd - The custom working directory to resolve relative paths from
|
|
420
|
+
* @returns {string}
|
|
421
|
+
* @throws {Error} When strictPaths is true and path would resolve outside the provided cwd
|
|
422
|
+
*/
|
|
423
|
+
getAbsolutePathWithCwd(filePath, cwd) {
|
|
424
|
+
if (this.isRelativePath(filePath)) {
|
|
425
|
+
const sanitizedPath = filePath.replace(/\0/g, "");
|
|
426
|
+
const resolved = import_node_path.default.resolve(cwd, sanitizedPath);
|
|
427
|
+
if (this._strictPaths) {
|
|
428
|
+
const normalizedResolved = import_node_path.default.normalize(resolved);
|
|
429
|
+
const normalizedCwd = import_node_path.default.normalize(cwd);
|
|
430
|
+
const isWithinCwd = normalizedResolved === normalizedCwd || normalizedResolved.startsWith(normalizedCwd + import_node_path.default.sep);
|
|
431
|
+
if (!isWithinCwd) {
|
|
432
|
+
throw new Error(
|
|
433
|
+
`Path traversal attempt blocked: "${filePath}" resolves outside of working directory "${cwd}"`
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return resolved;
|
|
379
438
|
}
|
|
380
439
|
return filePath;
|
|
381
440
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -2,35 +2,70 @@ import { Buffer } from 'node:buffer';
|
|
|
2
2
|
import { FlatCacheOptions, FlatCache } from 'flat-cache';
|
|
3
3
|
|
|
4
4
|
type FileEntryCacheOptions = {
|
|
5
|
+
/** Whether to use file modified time for change detection (default: true) */
|
|
5
6
|
useModifiedTime?: boolean;
|
|
7
|
+
/** Whether to use checksum for change detection (default: false) */
|
|
6
8
|
useCheckSum?: boolean;
|
|
9
|
+
/** Hash algorithm to use for checksum (default: 'md5') */
|
|
7
10
|
hashAlgorithm?: string;
|
|
11
|
+
/** Current working directory for resolving relative paths (default: process.cwd()) */
|
|
12
|
+
cwd?: string;
|
|
13
|
+
/** Restrict file access to within cwd boundaries (default: true) */
|
|
14
|
+
strictPaths?: boolean;
|
|
15
|
+
/** Options for the underlying flat cache */
|
|
8
16
|
cache?: FlatCacheOptions;
|
|
9
17
|
};
|
|
10
18
|
type GetFileDescriptorOptions = {
|
|
19
|
+
/** Whether to use checksum for this specific file check instead of modified time (mtime) (overrides instance setting) */
|
|
11
20
|
useCheckSum?: boolean;
|
|
12
|
-
useModifiedTime?: boolean;
|
|
13
21
|
};
|
|
14
22
|
type FileDescriptor = {
|
|
23
|
+
/** The cache key for this file (typically the file path) */
|
|
15
24
|
key: string;
|
|
25
|
+
/** Whether the file has changed since last cache check */
|
|
16
26
|
changed?: boolean;
|
|
27
|
+
/** Metadata about the file */
|
|
17
28
|
meta: FileDescriptorMeta;
|
|
29
|
+
/** Whether the file was not found */
|
|
18
30
|
notFound?: boolean;
|
|
31
|
+
/** Error encountered when accessing the file */
|
|
19
32
|
err?: Error;
|
|
20
33
|
};
|
|
21
34
|
type FileDescriptorMeta = {
|
|
35
|
+
/** File size in bytes */
|
|
22
36
|
size?: number;
|
|
37
|
+
/** File modification time (timestamp in milliseconds) */
|
|
23
38
|
mtime?: number;
|
|
39
|
+
/** File content hash (when useCheckSum is enabled) */
|
|
24
40
|
hash?: string;
|
|
41
|
+
/** Custom data associated with the file (e.g., lint results, metadata) */
|
|
25
42
|
data?: unknown;
|
|
26
43
|
};
|
|
27
44
|
type AnalyzedFiles = {
|
|
45
|
+
/** Array of file paths that have changed since last cache */
|
|
28
46
|
changedFiles: string[];
|
|
47
|
+
/** Array of file paths that were not found */
|
|
29
48
|
notFoundFiles: string[];
|
|
49
|
+
/** Array of file paths that have not changed since last cache */
|
|
30
50
|
notChangedFiles: string[];
|
|
31
51
|
};
|
|
32
|
-
|
|
33
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Create a new FileEntryCache instance from a file path
|
|
54
|
+
* @param filePath - The path to the cache file
|
|
55
|
+
* @param useCheckSum - Whether to use checksum to detect file changes (default: false)
|
|
56
|
+
* @param cwd - The current working directory for resolving relative paths (default: process.cwd())
|
|
57
|
+
* @returns A new FileEntryCache instance
|
|
58
|
+
*/
|
|
59
|
+
declare function createFromFile(filePath: string, useCheckSum?: boolean, cwd?: string): FileEntryCache;
|
|
60
|
+
/**
|
|
61
|
+
* Create a new FileEntryCache instance
|
|
62
|
+
* @param cacheId - The cache file name
|
|
63
|
+
* @param cacheDirectory - The directory to store the cache file (default: undefined, cache won't be persisted)
|
|
64
|
+
* @param useCheckSum - Whether to use checksum to detect file changes (default: false)
|
|
65
|
+
* @param cwd - The current working directory for resolving relative paths (default: process.cwd())
|
|
66
|
+
* @returns A new FileEntryCache instance
|
|
67
|
+
*/
|
|
68
|
+
declare function create(cacheId: string, cacheDirectory?: string, useCheckSum?: boolean, cwd?: string): FileEntryCache;
|
|
34
69
|
declare class FileEntryDefault {
|
|
35
70
|
static create: typeof create;
|
|
36
71
|
static createFromFile: typeof createFromFile;
|
|
@@ -38,11 +73,12 @@ declare class FileEntryDefault {
|
|
|
38
73
|
declare class FileEntryCache {
|
|
39
74
|
private _cache;
|
|
40
75
|
private _useCheckSum;
|
|
41
|
-
private _useModifiedTime;
|
|
42
76
|
private _hashAlgorithm;
|
|
77
|
+
private _cwd;
|
|
78
|
+
private _strictPaths;
|
|
43
79
|
/**
|
|
44
80
|
* Create a new FileEntryCache instance
|
|
45
|
-
* @param options - The options for the FileEntryCache
|
|
81
|
+
* @param options - The options for the FileEntryCache (all properties are optional with defaults)
|
|
46
82
|
*/
|
|
47
83
|
constructor(options?: FileEntryCacheOptions);
|
|
48
84
|
/**
|
|
@@ -57,7 +93,7 @@ declare class FileEntryCache {
|
|
|
57
93
|
set cache(cache: FlatCache);
|
|
58
94
|
/**
|
|
59
95
|
* Use the hash to check if the file has changed
|
|
60
|
-
* @returns {boolean} if the hash is used to check if the file has changed
|
|
96
|
+
* @returns {boolean} if the hash is used to check if the file has changed (default: false)
|
|
61
97
|
*/
|
|
62
98
|
get useCheckSum(): boolean;
|
|
63
99
|
/**
|
|
@@ -65,19 +101,9 @@ declare class FileEntryCache {
|
|
|
65
101
|
* @param {boolean} value - The value to set
|
|
66
102
|
*/
|
|
67
103
|
set useCheckSum(value: boolean);
|
|
68
|
-
/**
|
|
69
|
-
* Use the modified time to check if the file has changed
|
|
70
|
-
* @returns {boolean} if the modified time is used to check if the file has changed
|
|
71
|
-
*/
|
|
72
|
-
get useModifiedTime(): boolean;
|
|
73
|
-
/**
|
|
74
|
-
* Set the useModifiedTime value
|
|
75
|
-
* @param {boolean} value - The value to set
|
|
76
|
-
*/
|
|
77
|
-
set useModifiedTime(value: boolean);
|
|
78
104
|
/**
|
|
79
105
|
* Get the hash algorithm
|
|
80
|
-
* @returns {string} The hash algorithm
|
|
106
|
+
* @returns {string} The hash algorithm (default: 'md5')
|
|
81
107
|
*/
|
|
82
108
|
get hashAlgorithm(): string;
|
|
83
109
|
/**
|
|
@@ -85,6 +111,26 @@ declare class FileEntryCache {
|
|
|
85
111
|
* @param {string} value - The value to set
|
|
86
112
|
*/
|
|
87
113
|
set hashAlgorithm(value: string);
|
|
114
|
+
/**
|
|
115
|
+
* Get the current working directory
|
|
116
|
+
* @returns {string} The current working directory (default: process.cwd())
|
|
117
|
+
*/
|
|
118
|
+
get cwd(): string;
|
|
119
|
+
/**
|
|
120
|
+
* Set the current working directory
|
|
121
|
+
* @param {string} value - The value to set
|
|
122
|
+
*/
|
|
123
|
+
set cwd(value: string);
|
|
124
|
+
/**
|
|
125
|
+
* Get whether to restrict paths to cwd boundaries
|
|
126
|
+
* @returns {boolean} Whether strict path checking is enabled (default: true)
|
|
127
|
+
*/
|
|
128
|
+
get strictPaths(): boolean;
|
|
129
|
+
/**
|
|
130
|
+
* Set whether to restrict paths to cwd boundaries
|
|
131
|
+
* @param {boolean} value - The value to set
|
|
132
|
+
*/
|
|
133
|
+
set strictPaths(value: boolean);
|
|
88
134
|
/**
|
|
89
135
|
* Given a buffer, calculate md5 hash of its content.
|
|
90
136
|
* @method getHash
|
|
@@ -173,11 +219,23 @@ declare class FileEntryCache {
|
|
|
173
219
|
getFileDescriptorsByPath(filePath: string): FileDescriptor[];
|
|
174
220
|
/**
|
|
175
221
|
* Get the Absolute Path. If it is already absolute it will return the path as is.
|
|
222
|
+
* When strictPaths is enabled, ensures the resolved path stays within cwd boundaries.
|
|
176
223
|
* @method getAbsolutePath
|
|
177
224
|
* @param filePath - The file path to get the absolute path for
|
|
178
225
|
* @returns {string}
|
|
226
|
+
* @throws {Error} When strictPaths is true and path would resolve outside cwd
|
|
179
227
|
*/
|
|
180
228
|
getAbsolutePath(filePath: string): string;
|
|
229
|
+
/**
|
|
230
|
+
* Get the Absolute Path with a custom working directory. If it is already absolute it will return the path as is.
|
|
231
|
+
* When strictPaths is enabled, ensures the resolved path stays within the provided cwd boundaries.
|
|
232
|
+
* @method getAbsolutePathWithCwd
|
|
233
|
+
* @param filePath - The file path to get the absolute path for
|
|
234
|
+
* @param cwd - The custom working directory to resolve relative paths from
|
|
235
|
+
* @returns {string}
|
|
236
|
+
* @throws {Error} When strictPaths is true and path would resolve outside the provided cwd
|
|
237
|
+
*/
|
|
238
|
+
getAbsolutePathWithCwd(filePath: string, cwd: string): string;
|
|
181
239
|
/**
|
|
182
240
|
* Rename cache keys that start with a given path prefix.
|
|
183
241
|
* @method renameCacheKeys
|
package/dist/index.d.ts
CHANGED
|
@@ -2,35 +2,70 @@ import { Buffer } from 'node:buffer';
|
|
|
2
2
|
import { FlatCacheOptions, FlatCache } from 'flat-cache';
|
|
3
3
|
|
|
4
4
|
type FileEntryCacheOptions = {
|
|
5
|
+
/** Whether to use file modified time for change detection (default: true) */
|
|
5
6
|
useModifiedTime?: boolean;
|
|
7
|
+
/** Whether to use checksum for change detection (default: false) */
|
|
6
8
|
useCheckSum?: boolean;
|
|
9
|
+
/** Hash algorithm to use for checksum (default: 'md5') */
|
|
7
10
|
hashAlgorithm?: string;
|
|
11
|
+
/** Current working directory for resolving relative paths (default: process.cwd()) */
|
|
12
|
+
cwd?: string;
|
|
13
|
+
/** Restrict file access to within cwd boundaries (default: true) */
|
|
14
|
+
strictPaths?: boolean;
|
|
15
|
+
/** Options for the underlying flat cache */
|
|
8
16
|
cache?: FlatCacheOptions;
|
|
9
17
|
};
|
|
10
18
|
type GetFileDescriptorOptions = {
|
|
19
|
+
/** Whether to use checksum for this specific file check instead of modified time (mtime) (overrides instance setting) */
|
|
11
20
|
useCheckSum?: boolean;
|
|
12
|
-
useModifiedTime?: boolean;
|
|
13
21
|
};
|
|
14
22
|
type FileDescriptor = {
|
|
23
|
+
/** The cache key for this file (typically the file path) */
|
|
15
24
|
key: string;
|
|
25
|
+
/** Whether the file has changed since last cache check */
|
|
16
26
|
changed?: boolean;
|
|
27
|
+
/** Metadata about the file */
|
|
17
28
|
meta: FileDescriptorMeta;
|
|
29
|
+
/** Whether the file was not found */
|
|
18
30
|
notFound?: boolean;
|
|
31
|
+
/** Error encountered when accessing the file */
|
|
19
32
|
err?: Error;
|
|
20
33
|
};
|
|
21
34
|
type FileDescriptorMeta = {
|
|
35
|
+
/** File size in bytes */
|
|
22
36
|
size?: number;
|
|
37
|
+
/** File modification time (timestamp in milliseconds) */
|
|
23
38
|
mtime?: number;
|
|
39
|
+
/** File content hash (when useCheckSum is enabled) */
|
|
24
40
|
hash?: string;
|
|
41
|
+
/** Custom data associated with the file (e.g., lint results, metadata) */
|
|
25
42
|
data?: unknown;
|
|
26
43
|
};
|
|
27
44
|
type AnalyzedFiles = {
|
|
45
|
+
/** Array of file paths that have changed since last cache */
|
|
28
46
|
changedFiles: string[];
|
|
47
|
+
/** Array of file paths that were not found */
|
|
29
48
|
notFoundFiles: string[];
|
|
49
|
+
/** Array of file paths that have not changed since last cache */
|
|
30
50
|
notChangedFiles: string[];
|
|
31
51
|
};
|
|
32
|
-
|
|
33
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Create a new FileEntryCache instance from a file path
|
|
54
|
+
* @param filePath - The path to the cache file
|
|
55
|
+
* @param useCheckSum - Whether to use checksum to detect file changes (default: false)
|
|
56
|
+
* @param cwd - The current working directory for resolving relative paths (default: process.cwd())
|
|
57
|
+
* @returns A new FileEntryCache instance
|
|
58
|
+
*/
|
|
59
|
+
declare function createFromFile(filePath: string, useCheckSum?: boolean, cwd?: string): FileEntryCache;
|
|
60
|
+
/**
|
|
61
|
+
* Create a new FileEntryCache instance
|
|
62
|
+
* @param cacheId - The cache file name
|
|
63
|
+
* @param cacheDirectory - The directory to store the cache file (default: undefined, cache won't be persisted)
|
|
64
|
+
* @param useCheckSum - Whether to use checksum to detect file changes (default: false)
|
|
65
|
+
* @param cwd - The current working directory for resolving relative paths (default: process.cwd())
|
|
66
|
+
* @returns A new FileEntryCache instance
|
|
67
|
+
*/
|
|
68
|
+
declare function create(cacheId: string, cacheDirectory?: string, useCheckSum?: boolean, cwd?: string): FileEntryCache;
|
|
34
69
|
declare class FileEntryDefault {
|
|
35
70
|
static create: typeof create;
|
|
36
71
|
static createFromFile: typeof createFromFile;
|
|
@@ -38,11 +73,12 @@ declare class FileEntryDefault {
|
|
|
38
73
|
declare class FileEntryCache {
|
|
39
74
|
private _cache;
|
|
40
75
|
private _useCheckSum;
|
|
41
|
-
private _useModifiedTime;
|
|
42
76
|
private _hashAlgorithm;
|
|
77
|
+
private _cwd;
|
|
78
|
+
private _strictPaths;
|
|
43
79
|
/**
|
|
44
80
|
* Create a new FileEntryCache instance
|
|
45
|
-
* @param options - The options for the FileEntryCache
|
|
81
|
+
* @param options - The options for the FileEntryCache (all properties are optional with defaults)
|
|
46
82
|
*/
|
|
47
83
|
constructor(options?: FileEntryCacheOptions);
|
|
48
84
|
/**
|
|
@@ -57,7 +93,7 @@ declare class FileEntryCache {
|
|
|
57
93
|
set cache(cache: FlatCache);
|
|
58
94
|
/**
|
|
59
95
|
* Use the hash to check if the file has changed
|
|
60
|
-
* @returns {boolean} if the hash is used to check if the file has changed
|
|
96
|
+
* @returns {boolean} if the hash is used to check if the file has changed (default: false)
|
|
61
97
|
*/
|
|
62
98
|
get useCheckSum(): boolean;
|
|
63
99
|
/**
|
|
@@ -65,19 +101,9 @@ declare class FileEntryCache {
|
|
|
65
101
|
* @param {boolean} value - The value to set
|
|
66
102
|
*/
|
|
67
103
|
set useCheckSum(value: boolean);
|
|
68
|
-
/**
|
|
69
|
-
* Use the modified time to check if the file has changed
|
|
70
|
-
* @returns {boolean} if the modified time is used to check if the file has changed
|
|
71
|
-
*/
|
|
72
|
-
get useModifiedTime(): boolean;
|
|
73
|
-
/**
|
|
74
|
-
* Set the useModifiedTime value
|
|
75
|
-
* @param {boolean} value - The value to set
|
|
76
|
-
*/
|
|
77
|
-
set useModifiedTime(value: boolean);
|
|
78
104
|
/**
|
|
79
105
|
* Get the hash algorithm
|
|
80
|
-
* @returns {string} The hash algorithm
|
|
106
|
+
* @returns {string} The hash algorithm (default: 'md5')
|
|
81
107
|
*/
|
|
82
108
|
get hashAlgorithm(): string;
|
|
83
109
|
/**
|
|
@@ -85,6 +111,26 @@ declare class FileEntryCache {
|
|
|
85
111
|
* @param {string} value - The value to set
|
|
86
112
|
*/
|
|
87
113
|
set hashAlgorithm(value: string);
|
|
114
|
+
/**
|
|
115
|
+
* Get the current working directory
|
|
116
|
+
* @returns {string} The current working directory (default: process.cwd())
|
|
117
|
+
*/
|
|
118
|
+
get cwd(): string;
|
|
119
|
+
/**
|
|
120
|
+
* Set the current working directory
|
|
121
|
+
* @param {string} value - The value to set
|
|
122
|
+
*/
|
|
123
|
+
set cwd(value: string);
|
|
124
|
+
/**
|
|
125
|
+
* Get whether to restrict paths to cwd boundaries
|
|
126
|
+
* @returns {boolean} Whether strict path checking is enabled (default: true)
|
|
127
|
+
*/
|
|
128
|
+
get strictPaths(): boolean;
|
|
129
|
+
/**
|
|
130
|
+
* Set whether to restrict paths to cwd boundaries
|
|
131
|
+
* @param {boolean} value - The value to set
|
|
132
|
+
*/
|
|
133
|
+
set strictPaths(value: boolean);
|
|
88
134
|
/**
|
|
89
135
|
* Given a buffer, calculate md5 hash of its content.
|
|
90
136
|
* @method getHash
|
|
@@ -173,11 +219,23 @@ declare class FileEntryCache {
|
|
|
173
219
|
getFileDescriptorsByPath(filePath: string): FileDescriptor[];
|
|
174
220
|
/**
|
|
175
221
|
* Get the Absolute Path. If it is already absolute it will return the path as is.
|
|
222
|
+
* When strictPaths is enabled, ensures the resolved path stays within cwd boundaries.
|
|
176
223
|
* @method getAbsolutePath
|
|
177
224
|
* @param filePath - The file path to get the absolute path for
|
|
178
225
|
* @returns {string}
|
|
226
|
+
* @throws {Error} When strictPaths is true and path would resolve outside cwd
|
|
179
227
|
*/
|
|
180
228
|
getAbsolutePath(filePath: string): string;
|
|
229
|
+
/**
|
|
230
|
+
* Get the Absolute Path with a custom working directory. If it is already absolute it will return the path as is.
|
|
231
|
+
* When strictPaths is enabled, ensures the resolved path stays within the provided cwd boundaries.
|
|
232
|
+
* @method getAbsolutePathWithCwd
|
|
233
|
+
* @param filePath - The file path to get the absolute path for
|
|
234
|
+
* @param cwd - The custom working directory to resolve relative paths from
|
|
235
|
+
* @returns {string}
|
|
236
|
+
* @throws {Error} When strictPaths is true and path would resolve outside the provided cwd
|
|
237
|
+
*/
|
|
238
|
+
getAbsolutePathWithCwd(filePath: string, cwd: string): string;
|
|
181
239
|
/**
|
|
182
240
|
* Rename cache keys that start with a given path prefix.
|
|
183
241
|
* @method renameCacheKeys
|
package/dist/index.js
CHANGED
|
@@ -6,14 +6,15 @@ import {
|
|
|
6
6
|
createFromFile as createFlatCacheFile,
|
|
7
7
|
FlatCache
|
|
8
8
|
} from "flat-cache";
|
|
9
|
-
function createFromFile(filePath, useCheckSum) {
|
|
9
|
+
function createFromFile(filePath, useCheckSum, cwd) {
|
|
10
10
|
const fname = path.basename(filePath);
|
|
11
11
|
const directory = path.dirname(filePath);
|
|
12
|
-
return create(fname, directory, useCheckSum);
|
|
12
|
+
return create(fname, directory, useCheckSum, cwd);
|
|
13
13
|
}
|
|
14
|
-
function create(cacheId, cacheDirectory, useCheckSum) {
|
|
14
|
+
function create(cacheId, cacheDirectory, useCheckSum, cwd) {
|
|
15
15
|
const options = {
|
|
16
16
|
useCheckSum,
|
|
17
|
+
cwd,
|
|
17
18
|
cache: {
|
|
18
19
|
cacheId,
|
|
19
20
|
cacheDir: cacheDirectory
|
|
@@ -35,25 +36,29 @@ var FileEntryDefault = class {
|
|
|
35
36
|
var FileEntryCache = class {
|
|
36
37
|
_cache = new FlatCache({ useClone: false });
|
|
37
38
|
_useCheckSum = false;
|
|
38
|
-
_useModifiedTime = true;
|
|
39
39
|
_hashAlgorithm = "md5";
|
|
40
|
+
_cwd = process.cwd();
|
|
41
|
+
_strictPaths = true;
|
|
40
42
|
/**
|
|
41
43
|
* Create a new FileEntryCache instance
|
|
42
|
-
* @param options - The options for the FileEntryCache
|
|
44
|
+
* @param options - The options for the FileEntryCache (all properties are optional with defaults)
|
|
43
45
|
*/
|
|
44
46
|
constructor(options) {
|
|
45
47
|
if (options?.cache) {
|
|
46
48
|
this._cache = new FlatCache(options.cache);
|
|
47
49
|
}
|
|
48
|
-
if (options?.useModifiedTime) {
|
|
49
|
-
this._useModifiedTime = options.useModifiedTime;
|
|
50
|
-
}
|
|
51
50
|
if (options?.useCheckSum) {
|
|
52
51
|
this._useCheckSum = options.useCheckSum;
|
|
53
52
|
}
|
|
54
53
|
if (options?.hashAlgorithm) {
|
|
55
54
|
this._hashAlgorithm = options.hashAlgorithm;
|
|
56
55
|
}
|
|
56
|
+
if (options?.cwd) {
|
|
57
|
+
this._cwd = options.cwd;
|
|
58
|
+
}
|
|
59
|
+
if (options?.strictPaths !== void 0) {
|
|
60
|
+
this._strictPaths = options.strictPaths;
|
|
61
|
+
}
|
|
57
62
|
}
|
|
58
63
|
/**
|
|
59
64
|
* Get the cache
|
|
@@ -71,7 +76,7 @@ var FileEntryCache = class {
|
|
|
71
76
|
}
|
|
72
77
|
/**
|
|
73
78
|
* Use the hash to check if the file has changed
|
|
74
|
-
* @returns {boolean} if the hash is used to check if the file has changed
|
|
79
|
+
* @returns {boolean} if the hash is used to check if the file has changed (default: false)
|
|
75
80
|
*/
|
|
76
81
|
get useCheckSum() {
|
|
77
82
|
return this._useCheckSum;
|
|
@@ -83,23 +88,9 @@ var FileEntryCache = class {
|
|
|
83
88
|
set useCheckSum(value) {
|
|
84
89
|
this._useCheckSum = value;
|
|
85
90
|
}
|
|
86
|
-
/**
|
|
87
|
-
* Use the modified time to check if the file has changed
|
|
88
|
-
* @returns {boolean} if the modified time is used to check if the file has changed
|
|
89
|
-
*/
|
|
90
|
-
get useModifiedTime() {
|
|
91
|
-
return this._useModifiedTime;
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Set the useModifiedTime value
|
|
95
|
-
* @param {boolean} value - The value to set
|
|
96
|
-
*/
|
|
97
|
-
set useModifiedTime(value) {
|
|
98
|
-
this._useModifiedTime = value;
|
|
99
|
-
}
|
|
100
91
|
/**
|
|
101
92
|
* Get the hash algorithm
|
|
102
|
-
* @returns {string} The hash algorithm
|
|
93
|
+
* @returns {string} The hash algorithm (default: 'md5')
|
|
103
94
|
*/
|
|
104
95
|
get hashAlgorithm() {
|
|
105
96
|
return this._hashAlgorithm;
|
|
@@ -111,6 +102,34 @@ var FileEntryCache = class {
|
|
|
111
102
|
set hashAlgorithm(value) {
|
|
112
103
|
this._hashAlgorithm = value;
|
|
113
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Get the current working directory
|
|
107
|
+
* @returns {string} The current working directory (default: process.cwd())
|
|
108
|
+
*/
|
|
109
|
+
get cwd() {
|
|
110
|
+
return this._cwd;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Set the current working directory
|
|
114
|
+
* @param {string} value - The value to set
|
|
115
|
+
*/
|
|
116
|
+
set cwd(value) {
|
|
117
|
+
this._cwd = value;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Get whether to restrict paths to cwd boundaries
|
|
121
|
+
* @returns {boolean} Whether strict path checking is enabled (default: true)
|
|
122
|
+
*/
|
|
123
|
+
get strictPaths() {
|
|
124
|
+
return this._strictPaths;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Set whether to restrict paths to cwd boundaries
|
|
128
|
+
* @param {boolean} value - The value to set
|
|
129
|
+
*/
|
|
130
|
+
set strictPaths(value) {
|
|
131
|
+
this._strictPaths = value;
|
|
132
|
+
}
|
|
114
133
|
/**
|
|
115
134
|
* Given a buffer, calculate md5 hash of its content.
|
|
116
135
|
* @method getHash
|
|
@@ -207,7 +226,6 @@ var FileEntryCache = class {
|
|
|
207
226
|
result.meta = this._cache.getKey(result.key) ?? {};
|
|
208
227
|
const absolutePath = this.getAbsolutePath(filePath);
|
|
209
228
|
const useCheckSumValue = options?.useCheckSum ?? this._useCheckSum;
|
|
210
|
-
const useModifiedTimeValue = options?.useModifiedTime ?? this._useModifiedTime;
|
|
211
229
|
try {
|
|
212
230
|
fstat = fs.statSync(absolutePath);
|
|
213
231
|
result.meta = {
|
|
@@ -240,7 +258,7 @@ var FileEntryCache = class {
|
|
|
240
258
|
if (result.meta.data === void 0) {
|
|
241
259
|
result.meta.data = metaCache.data;
|
|
242
260
|
}
|
|
243
|
-
if (
|
|
261
|
+
if (useCheckSumValue === false && metaCache?.mtime !== result.meta?.mtime) {
|
|
244
262
|
result.changed = true;
|
|
245
263
|
}
|
|
246
264
|
if (metaCache?.size !== result.meta?.size) {
|
|
@@ -335,13 +353,54 @@ var FileEntryCache = class {
|
|
|
335
353
|
}
|
|
336
354
|
/**
|
|
337
355
|
* Get the Absolute Path. If it is already absolute it will return the path as is.
|
|
356
|
+
* When strictPaths is enabled, ensures the resolved path stays within cwd boundaries.
|
|
338
357
|
* @method getAbsolutePath
|
|
339
358
|
* @param filePath - The file path to get the absolute path for
|
|
340
359
|
* @returns {string}
|
|
360
|
+
* @throws {Error} When strictPaths is true and path would resolve outside cwd
|
|
341
361
|
*/
|
|
342
362
|
getAbsolutePath(filePath) {
|
|
343
363
|
if (this.isRelativePath(filePath)) {
|
|
344
|
-
|
|
364
|
+
const sanitizedPath = filePath.replace(/\0/g, "");
|
|
365
|
+
const resolved = path.resolve(this._cwd, sanitizedPath);
|
|
366
|
+
if (this._strictPaths) {
|
|
367
|
+
const normalizedResolved = path.normalize(resolved);
|
|
368
|
+
const normalizedCwd = path.normalize(this._cwd);
|
|
369
|
+
const isWithinCwd = normalizedResolved === normalizedCwd || normalizedResolved.startsWith(normalizedCwd + path.sep);
|
|
370
|
+
if (!isWithinCwd) {
|
|
371
|
+
throw new Error(
|
|
372
|
+
`Path traversal attempt blocked: "${filePath}" resolves outside of working directory "${this._cwd}"`
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return resolved;
|
|
377
|
+
}
|
|
378
|
+
return filePath;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Get the Absolute Path with a custom working directory. If it is already absolute it will return the path as is.
|
|
382
|
+
* When strictPaths is enabled, ensures the resolved path stays within the provided cwd boundaries.
|
|
383
|
+
* @method getAbsolutePathWithCwd
|
|
384
|
+
* @param filePath - The file path to get the absolute path for
|
|
385
|
+
* @param cwd - The custom working directory to resolve relative paths from
|
|
386
|
+
* @returns {string}
|
|
387
|
+
* @throws {Error} When strictPaths is true and path would resolve outside the provided cwd
|
|
388
|
+
*/
|
|
389
|
+
getAbsolutePathWithCwd(filePath, cwd) {
|
|
390
|
+
if (this.isRelativePath(filePath)) {
|
|
391
|
+
const sanitizedPath = filePath.replace(/\0/g, "");
|
|
392
|
+
const resolved = path.resolve(cwd, sanitizedPath);
|
|
393
|
+
if (this._strictPaths) {
|
|
394
|
+
const normalizedResolved = path.normalize(resolved);
|
|
395
|
+
const normalizedCwd = path.normalize(cwd);
|
|
396
|
+
const isWithinCwd = normalizedResolved === normalizedCwd || normalizedResolved.startsWith(normalizedCwd + path.sep);
|
|
397
|
+
if (!isWithinCwd) {
|
|
398
|
+
throw new Error(
|
|
399
|
+
`Path traversal attempt blocked: "${filePath}" resolves outside of working directory "${cwd}"`
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return resolved;
|
|
345
404
|
}
|
|
346
405
|
return filePath;
|
|
347
406
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "file-entry-cache",
|
|
3
|
-
"version": "11.0.0-beta.
|
|
3
|
+
"version": "11.0.0-beta.3",
|
|
4
4
|
"description": "A lightweight cache for file metadata, ideal for processes that work on a specific set of files and only need to reprocess files that have changed since the last run",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -29,16 +29,16 @@
|
|
|
29
29
|
"cache"
|
|
30
30
|
],
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@biomejs/biome": "^2.2.
|
|
33
|
-
"@types/node": "^24.
|
|
32
|
+
"@biomejs/biome": "^2.2.5",
|
|
33
|
+
"@types/node": "^24.6.2",
|
|
34
34
|
"@vitest/coverage-v8": "^3.2.4",
|
|
35
35
|
"rimraf": "^6.0.1",
|
|
36
36
|
"tsup": "^8.5.0",
|
|
37
|
-
"typescript": "^5.9.
|
|
37
|
+
"typescript": "^5.9.3",
|
|
38
38
|
"vitest": "^3.2.4"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"flat-cache": "^6.1.
|
|
41
|
+
"flat-cache": "^6.1.17"
|
|
42
42
|
},
|
|
43
43
|
"files": [
|
|
44
44
|
"dist",
|