Skip to content

Commit 56f00b3

Browse files
authored
file-entry-cache - feat: renaming strictPaths to restrictAccessToCwd (#1397)
* file-entry-cache - feat: renaming strictPaths to restrictAccessToCwd * create * Update README.md * Update README.md * useAbsolutePathAsKey * logger
1 parent 2d90d08 commit 56f00b3

File tree

5 files changed

+192
-93
lines changed

5 files changed

+192
-93
lines changed

packages/file-entry-cache/README.md

Lines changed: 139 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,23 @@ fileDescriptor = cache.getFileDescriptor('./src/file.txt');
7272
console.log(fileDescriptor.changed); // true
7373
```
7474

75+
Using `create()` with options for more control:
76+
```javascript
77+
import fileEntryCache from 'file-entry-cache';
78+
79+
// Create cache with options
80+
const cache = fileEntryCache.create('myCache', './.cache', {
81+
useCheckSum: true, // Use checksums for more reliable change detection
82+
cwd: '/path/to/project', // Custom working directory
83+
restrictAccessToCwd: false, // Allow access outside cwd (use with caution)
84+
useAbsolutePathAsKey: false // Store relative paths in cache
85+
});
86+
87+
let fileDescriptor = cache.getFileDescriptor('./src/file.txt');
88+
console.log(fileDescriptor.changed); // true
89+
console.log(fileDescriptor.meta.hash); // checksum hash
90+
```
91+
7592
Save it to Disk and Reconcile files that are no longer found
7693
```javascript
7794
import fileEntryCache from 'file-entry-cache';
@@ -85,20 +102,80 @@ Load the cache from a file:
85102

86103
```javascript
87104
import fileEntryCache from 'file-entry-cache';
105+
106+
// Basic usage
88107
const cache = fileEntryCache.createFromFile('/path/to/cache/file');
89108
let fileDescriptor = cache.getFileDescriptor('./src/file.txt');
90109
console.log(fileDescriptor.changed); // false as it has not changed from the saved cache.
110+
111+
// With options
112+
const cache2 = fileEntryCache.createFromFile('/path/to/cache/file', {
113+
useCheckSum: true,
114+
cwd: '/path/to/project'
115+
});
91116
```
92117

93118

94119
# Changes from v10 to v11
95120

96121
**BREAKING CHANGES:**
97-
- **`strictPaths` now defaults to `true`** - Path traversal protection is enabled by default for security. To restore v10 behavior, explicitly set `strictPaths: false`
122+
123+
- **`create()` and `createFromFile()` now use `CreateOptions` object** - The function signatures have changed to accept an options object instead of individual parameters for better extensibility and clarity.
124+
125+
**Old API (v10):**
126+
```javascript
127+
// v10 - positional parameters
128+
const cache = fileEntryCache.create(cacheId, cacheDirectory, useCheckSum, cwd);
129+
const cache2 = fileEntryCache.createFromFile(filePath, useCheckSum, cwd);
130+
```
131+
132+
**New API (v11):**
133+
```javascript
134+
// v11 - options object
135+
const cache = fileEntryCache.create(cacheId, cacheDirectory, {
136+
useCheckSum: true,
137+
cwd: '/path/to/project',
138+
restrictAccessToCwd: false
139+
});
140+
141+
const cache2 = fileEntryCache.createFromFile(filePath, {
142+
useCheckSum: true,
143+
cwd: '/path/to/project',
144+
restrictAccessToCwd: false
145+
});
146+
```
147+
148+
- **Renamed `strictPaths` to `restrictAccessToCwd`** - For better clarity and self-documentation, the option that restricts file access to the current working directory has been renamed.
149+
150+
**Migration:**
151+
```javascript
152+
// Old
153+
const cache = new FileEntryCache({ strictPaths: true });
154+
cache.strictPaths = false;
155+
156+
// New
157+
const cache = new FileEntryCache({ restrictAccessToCwd: true });
158+
cache.restrictAccessToCwd = false;
159+
```
160+
161+
- **Renamed `currentWorkingDirectory` to `cwd`** - For consistency with common conventions and brevity, the property has been renamed to `cwd`.
162+
163+
**Migration:**
164+
```javascript
165+
// Old
166+
const cache = new FileEntryCache({ currentWorkingDirectory: '/path/to/project' });
167+
cache.currentWorkingDirectory = '/new/path';
168+
169+
// New
170+
const cache = new FileEntryCache({ cwd: '/path/to/project' });
171+
cache.cwd = '/new/path';
172+
```
98173

99174
**NEW FEATURES:**
100175
- **Added `cwd` option** - You can now specify a custom current working directory for resolving relative paths
101-
- **Added `strictPaths` option** - Provides protection against path traversal attacks (enabled by default)
176+
- **Added `restrictAccessToCwd` option** - Provides protection against path traversal attacks (disabled by default for backwards compatibility)
177+
- **Added `useAbsolutePathAsKey` option** - When `true`, cache keys use absolute paths instead of the provided paths. Default is `false` for better cache portability with relative paths
178+
- **Added `logger` option** - Support for Pino-compatible logger instances to enable debugging and monitoring of cache operations. See [Logger Support](#logger-support) section for details
102179
- **Improved cache portability** - When using relative paths with the same `cwd`, cache files are portable across different environments
103180

104181
# Changes from v9 to v10
@@ -114,15 +191,24 @@ There have been many features added and changes made to the `file-entry-cache` c
114191
- 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`.
115192

116193
# Global Default Functions
117-
- `create(cacheId: string, cacheDirectory?: string, useCheckSum?: boolean, cwd?: string)` - Creates a new instance of the `FileEntryCache` class
118-
- `createFromFile(cachePath: string, useCheckSum?: boolean, cwd?: string)` - Creates a new instance of the `FileEntryCache` class and loads the cache from a file.
194+
- `create(cacheId: string, cacheDirectory?: string, options?: CreateOptions)` - Creates a new instance of the `FileEntryCache` class
195+
- `createFromFile(filePath: string, options?: CreateOptions)` - Creates a new instance of the `FileEntryCache` class and loads the cache from a file.
196+
197+
## CreateOptions Type
198+
All options from `FileEntryCacheOptions` except `cache`:
199+
- `useCheckSum?` - If `true` it will use a checksum to determine if the file has changed. Default is `false`
200+
- `hashAlgorithm?` - The algorithm to use for the checksum. Default is `md5`
201+
- `cwd?` - The current working directory for resolving relative paths. Default is `process.cwd()`
202+
- `restrictAccessToCwd?` - If `true` restricts file access to within `cwd` boundaries. Default is `false`
203+
- `useAbsolutePathAsKey?` - If `true` uses absolute paths as cache keys. Default is `false`
204+
- `logger?` - A logger instance for debugging. Default is `undefined`
119205

120206
# FileEntryCache Options (FileEntryCacheOptions)
121207
- `useModifiedTime?` - If `true` it will use the modified time to determine if the file has changed. Default is `true`
122208
- `useCheckSum?` - If `true` it will use a checksum to determine if the file has changed. Default is `false`
123209
- `hashAlgorithm?` - The algorithm to use for the checksum. Default is `md5` but can be any algorithm supported by `crypto.createHash`
124210
- `cwd?` - The current working directory for resolving relative paths. Default is `process.cwd()`
125-
- `strictPaths?` - If `true` restricts file access to within `cwd` boundaries, preventing path traversal attacks. Default is `true`
211+
- `restrictAccessToCwd?` - If `true` restricts file access to within `cwd` boundaries, preventing path traversal attacks. Default is `true`
126212
- `logger?` - A logger instance compatible with Pino logger interface for debugging and monitoring. Default is `undefined`
127213
- `cache.ttl?` - The time to live for the cache in milliseconds. Default is `0` which means no expiration
128214
- `cache.lruSize?` - The number of items to keep in the cache. Default is `0` which means no limit
@@ -141,9 +227,10 @@ There have been many features added and changes made to the `file-entry-cache` c
141227
- `hashAlgorithm: string` - The algorithm to use for the checksum. Default is `md5` but can be any algorithm supported by `crypto.createHash`
142228
- `getHash(buffer: Buffer): string` - Gets the hash of a buffer used for checksums
143229
- `cwd: string` - The current working directory for resolving relative paths. Default is `process.cwd()`
144-
- `strictPaths: boolean` - If `true` restricts file access to within `cwd` boundaries. Default is `true`
230+
- `restrictAccessToCwd: boolean` - If `true` restricts file access to within `cwd` boundaries. Default is `true`
231+
- `useAbsolutePathAsKey: boolean` - If `true` uses absolute paths as cache keys. Default is `false` to maintain better cache portability
145232
- `logger: ILogger | undefined` - A logger instance for debugging and monitoring cache operations
146-
- `createFileKey(filePath: string): string` - Returns the cache key for the file path (returns the path exactly as provided).
233+
- `createFileKey(filePath: string): string` - Returns the cache key for the file path (returns the path exactly as provided when `useAbsolutePathAsKey` is `false`, otherwise returns the absolute path).
147234
- `deleteCacheFile(): boolean` - Deletes the cache file from disk
148235
- `destroy(): void` - Destroys the cache. This will clear the cache in memory. If using cache persistence it will stop the interval.
149236
- `removeEntry(filePath: string): void` - Removes an entry from the cache.
@@ -154,8 +241,8 @@ There have been many features added and changes made to the `file-entry-cache` c
154241
- `analyzeFiles(files: string[])` will return `AnalyzedFiles` object with `changedFiles`, `notFoundFiles`, and `notChangedFiles` as FileDescriptor arrays.
155242
- `getUpdatedFiles(files: string[])` will return an array of `FileEntryDescriptor` objects that have changed.
156243
- `getFileDescriptorsByPath(filePath: string): FileEntryDescriptor[]` will return an array of `FileEntryDescriptor` objects that starts with the path prefix specified.
157-
- `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`.
158-
- `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`.
244+
- `getAbsolutePath(filePath: string): string` - Resolves a relative path to absolute using the configured `cwd`. Returns absolute paths unchanged. When `restrictAccessToCwd` is enabled, throws an error if the path resolves outside `cwd`.
245+
- `getAbsolutePathWithCwd(filePath: string, cwd: string): string` - Resolves a relative path to absolute using a custom working directory. When `restrictAccessToCwd` is enabled, throws an error if the path resolves outside the provided `cwd`.
159246

160247
# Get File Descriptor
161248

@@ -175,9 +262,12 @@ The cache stores paths exactly as they are provided (relative or absolute). When
175262
// Default: uses process.cwd()
176263
const cache1 = fileEntryCache.create('cache1');
177264

178-
// Custom working directory
179-
const cache2 = fileEntryCache.create('cache2', './cache', false, '/project/root');
180-
// Or with options object
265+
// Custom working directory using options object
266+
const cache2 = fileEntryCache.create('cache2', './cache', {
267+
cwd: '/project/root'
268+
});
269+
270+
// Or using the class constructor directly
181271
const cache3 = new FileEntryCache({ cwd: '/project/root' });
182272

183273
// The cache key is always the provided path
@@ -192,12 +282,16 @@ Using relative paths with a consistent `cwd` (defaults to `process.cwd()`) makes
192282

193283
```javascript
194284
// On machine A (project at /home/user/project)
195-
const cacheA = fileEntryCache.create('build-cache', './cache', false, '/home/user/project');
285+
const cacheA = fileEntryCache.create('build-cache', './cache', {
286+
cwd: '/home/user/project'
287+
});
196288
cacheA.getFileDescriptor('./src/index.js'); // Resolves to /home/user/project/src/index.js
197289
cacheA.reconcile();
198290

199291
// On machine B (project at /workspace/project)
200-
const cacheB = fileEntryCache.create('build-cache', './cache', false, '/workspace/project');
292+
const cacheB = fileEntryCache.create('build-cache', './cache', {
293+
cwd: '/workspace/project'
294+
});
201295
cacheB.getFileDescriptor('./src/index.js'); // Resolves to /workspace/project/src/index.js
202296
// Cache hit! File hasn't changed since machine A
203297
```
@@ -208,11 +302,9 @@ For maximum cache portability across different environments, use checksums (`use
208302

209303
```javascript
210304
// Development machine
211-
const devCache = fileEntryCache.create(
212-
'.buildcache',
213-
'./cache', // cache directory
214-
true // Use checksums for content-based comparison
215-
);
305+
const devCache = fileEntryCache.create('.buildcache', './cache', {
306+
useCheckSum: true // Use checksums for content-based comparison
307+
});
216308

217309
// Process files using relative paths
218310
const descriptor = devCache.getFileDescriptor('./src/index.js');
@@ -223,12 +315,10 @@ if (descriptor.changed) {
223315
devCache.reconcile(); // Save cache
224316

225317
// CI/CD Pipeline or another developer's machine
226-
const ciCache = fileEntryCache.create(
227-
'.buildcache',
228-
'./node_modules/.cache',
229-
true, // Same checksum setting
230-
process.cwd() // Different absolute path, same relative structure
231-
);
318+
const ciCache = fileEntryCache.create('.buildcache', './node_modules/.cache', {
319+
useCheckSum: true, // Same checksum setting
320+
cwd: process.cwd() // Different absolute path, same relative structure
321+
});
232322

233323
// Same relative path works across environments
234324
const descriptor2 = ciCache.getFileDescriptor('./src/index.js');
@@ -244,12 +334,18 @@ Cache remains valid even when projects are moved or renamed:
244334

245335
```javascript
246336
// Original location: /projects/my-app
247-
const cache1 = fileEntryCache.create('.cache', './cache', true, '/projects/my-app');
337+
const cache1 = fileEntryCache.create('.cache', './cache', {
338+
useCheckSum: true,
339+
cwd: '/projects/my-app'
340+
});
248341
cache1.getFileDescriptor('./src/app.js');
249342
cache1.reconcile();
250343

251344
// After moving project to: /archived/2024/my-app
252-
const cache2 = fileEntryCache.create('.cache', './cache', true, '/archived/2024/my-app');
345+
const cache2 = fileEntryCache.create('.cache', './cache', {
346+
useCheckSum: true,
347+
cwd: '/archived/2024/my-app'
348+
});
253349
cache2.getFileDescriptor('./src/app.js'); // Still finds cached entry!
254350
// Cache valid as long as relative structure unchanged
255351
```
@@ -270,12 +366,12 @@ if (fileDescriptor.notFound) {
270366

271367
# Path Security and Traversal Prevention
272368

273-
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.
369+
The `restrictAccessToCwd` 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.
274370

275371
## Basic Usage
276372

277373
```javascript
278-
// strictPaths is enabled by default for security
374+
// restrictAccessToCwd is enabled by default for security
279375
const cache = new FileEntryCache({
280376
cwd: '/project/root'
281377
});
@@ -293,13 +389,13 @@ try {
293389
// To allow parent directory access (not recommended for untrusted input)
294390
const unsafeCache = new FileEntryCache({
295391
cwd: '/project/root',
296-
strictPaths: false // Explicitly disable protection
392+
restrictAccessToCwd: false // Explicitly disable protection
297393
});
298394
```
299395

300396
## Security Features
301397

302-
When `strictPaths` is enabled:
398+
When `restrictAccessToCwd` is enabled:
303399
- **Path Traversal Prevention**: Blocks attempts to access files outside the working directory using `../` sequences
304400
- **Null Byte Protection**: Automatically removes null bytes from paths to prevent injection attacks
305401
- **Path Normalization**: Cleans and normalizes paths to prevent bypass attempts
@@ -309,15 +405,11 @@ When `strictPaths` is enabled:
309405
### Build Tools with Untrusted Input
310406
```javascript
311407
// Secure build tool configuration
312-
const cache = fileEntryCache.create(
313-
'.buildcache',
314-
'./cache',
315-
true, // useCheckSum
316-
process.cwd()
317-
);
318-
319-
// Enable strict path checking for security
320-
cache.strictPaths = true;
408+
const cache = fileEntryCache.create('.buildcache', './cache', {
409+
useCheckSum: true,
410+
cwd: process.cwd(),
411+
restrictAccessToCwd: true // Enable strict path checking for security
412+
});
321413

322414
// Process user-provided file paths safely
323415
function processUserFile(userProvidedPath) {
@@ -340,7 +432,7 @@ function processUserFile(userProvidedPath) {
340432
// Strict security for CI/CD pipelines
341433
const cache = new FileEntryCache({
342434
cwd: process.env.GITHUB_WORKSPACE || process.cwd(),
343-
strictPaths: true, // Prevent access outside workspace
435+
restrictAccessToCwd: true, // Prevent access outside workspace
344436
useCheckSum: true // Content-based validation
345437
});
346438

@@ -355,20 +447,20 @@ cache.getFileDescriptor('../../../root'); // ✗ Blocked (path traversal)
355447
const cache = new FileEntryCache({ cwd: '/safe/directory' });
356448

357449
// Start with relaxed mode for trusted operations
358-
cache.strictPaths = false;
450+
cache.restrictAccessToCwd = false;
359451
processInternalFiles();
360452

361453
// Enable strict mode for untrusted input
362-
cache.strictPaths = true;
454+
cache.restrictAccessToCwd = true;
363455
processUserUploadedPaths();
364456

365457
// Return to relaxed mode if needed
366-
cache.strictPaths = false;
458+
cache.restrictAccessToCwd = false;
367459
```
368460

369461
## Default Behavior
370462

371-
**As of v11, `strictPaths` is enabled by default** to provide secure defaults. This means:
463+
**As of v11, `restrictAccessToCwd` is enabled by default** to provide secure defaults. This means:
372464
- Path traversal attempts using `../` are blocked
373465
- File access is restricted to within the configured `cwd`
374466
- Null bytes in paths are automatically sanitized
@@ -380,11 +472,11 @@ If you're upgrading from v10 or earlier and need to maintain the previous behavi
380472
```javascript
381473
const cache = new FileEntryCache({
382474
cwd: process.cwd(),
383-
strictPaths: false // Restore v10 behavior
475+
restrictAccessToCwd: false // Restore v10 behavior
384476
});
385477
```
386478

387-
However, we strongly recommend keeping `strictPaths: true` and adjusting your code to work within the security boundaries, especially when processing any untrusted input.
479+
However, we strongly recommend keeping `restrictAccessToCwd: true` and adjusting your code to work within the security boundaries, especially when processing any untrusted input.
388480

389481
# Using Checksums to Determine if a File has Changed (useCheckSum)
390482

0 commit comments

Comments
 (0)