@crawlee/memory-storage 3.17.0 → 3.17.1-beta.1
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/cache-helpers.js
CHANGED
|
@@ -18,7 +18,7 @@ async function findOrCacheDatasetByPossibleId(client, entryNameOrId) {
|
|
|
18
18
|
if (found) {
|
|
19
19
|
return found;
|
|
20
20
|
}
|
|
21
|
-
const datasetDir = (0,
|
|
21
|
+
const datasetDir = (0, utils_1.resolveWithinDirectory)(client.datasetsDirectory, entryNameOrId);
|
|
22
22
|
try {
|
|
23
23
|
// Check if directory exists
|
|
24
24
|
await (0, promises_1.access)(datasetDir);
|
|
@@ -99,7 +99,7 @@ async function findOrCacheKeyValueStoreByPossibleId(client, entryNameOrId) {
|
|
|
99
99
|
if (found) {
|
|
100
100
|
return found;
|
|
101
101
|
}
|
|
102
|
-
const keyValueStoreDir = (0,
|
|
102
|
+
const keyValueStoreDir = (0, utils_1.resolveWithinDirectory)(client.keyValueStoresDirectory, entryNameOrId);
|
|
103
103
|
try {
|
|
104
104
|
// Check if directory exists
|
|
105
105
|
await (0, promises_1.access)(keyValueStoreDir);
|
|
@@ -219,7 +219,7 @@ async function findRequestQueueByPossibleId(client, entryNameOrId) {
|
|
|
219
219
|
if (found) {
|
|
220
220
|
return found;
|
|
221
221
|
}
|
|
222
|
-
const requestQueueDir = (0,
|
|
222
|
+
const requestQueueDir = (0, utils_1.resolveWithinDirectory)(client.requestQueuesDirectory, entryNameOrId);
|
|
223
223
|
try {
|
|
224
224
|
// Check if directory exists
|
|
225
225
|
await (0, promises_1.access)(requestQueueDir);
|
package/fs/key-value-store/fs.js
CHANGED
|
@@ -60,7 +60,7 @@ class KeyValueFileSystemEntry {
|
|
|
60
60
|
catch {
|
|
61
61
|
try {
|
|
62
62
|
// Try without extension
|
|
63
|
-
file = await (0, promises_1.readFile)((0,
|
|
63
|
+
file = await (0, promises_1.readFile)((0, utils_1.resolveWithinDirectory)(this.storeDirectory, this.rawRecord.key));
|
|
64
64
|
utils_1.memoryStorageLog.warning([
|
|
65
65
|
`Key-value entry "${this.rawRecord.key}" for store ${(0, win32_1.basename)(this.storeDirectory)} does not have a file extension, assuming it as text.`,
|
|
66
66
|
'If you want to have correct interpretation of the file, you should add a file extension to the entry.',
|
|
@@ -88,8 +88,8 @@ class KeyValueFileSystemEntry {
|
|
|
88
88
|
contentType && data.contentType && contentType.startsWith(data.contentType)
|
|
89
89
|
? data.key
|
|
90
90
|
: `${data.key}.${data.extension}`;
|
|
91
|
-
this.filePath ?? (this.filePath = (0,
|
|
92
|
-
this.fileMetadataPath ?? (this.fileMetadataPath = (0,
|
|
91
|
+
this.filePath ?? (this.filePath = (0, utils_1.resolveWithinDirectory)(this.storeDirectory, fileName));
|
|
92
|
+
this.fileMetadataPath ?? (this.fileMetadataPath = (0, utils_1.resolveWithinDirectory)(this.storeDirectory, `${data.key}.__metadata__.json`));
|
|
93
93
|
const { value, ...rest } = data;
|
|
94
94
|
this.rawRecord = rest;
|
|
95
95
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crawlee/memory-storage",
|
|
3
|
-
"version": "3.17.
|
|
3
|
+
"version": "3.17.1-beta.1",
|
|
4
4
|
"description": "A simple in-memory storage implementation of the Apify API",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">= 16"
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@apify/log": "^2.4.0",
|
|
52
|
-
"@crawlee/types": "3.17.
|
|
52
|
+
"@crawlee/types": "3.17.1-beta.1",
|
|
53
53
|
"@sapphire/async-queue": "^1.5.0",
|
|
54
54
|
"@sapphire/shapeshift": "^3.0.0",
|
|
55
55
|
"content-type": "^1.0.4",
|
|
@@ -67,5 +67,5 @@
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
},
|
|
70
|
-
"gitHead": "
|
|
70
|
+
"gitHead": "3d45254f3d305c58b097f3b73b290c09bc664fe6"
|
|
71
71
|
}
|
|
@@ -4,7 +4,6 @@ exports.DatasetClient = void 0;
|
|
|
4
4
|
/* eslint-disable import/no-duplicates */
|
|
5
5
|
const node_crypto_1 = require("node:crypto");
|
|
6
6
|
const promises_1 = require("node:fs/promises");
|
|
7
|
-
const node_path_1 = require("node:path");
|
|
8
7
|
const shapeshift_1 = require("@sapphire/shapeshift");
|
|
9
8
|
const fs_extra_1 = require("fs-extra");
|
|
10
9
|
const background_handler_1 = require("../background-handler");
|
|
@@ -75,7 +74,7 @@ class DatasetClient extends base_client_1.BaseClient {
|
|
|
75
74
|
value: void 0
|
|
76
75
|
});
|
|
77
76
|
this.name = options.name;
|
|
78
|
-
this.datasetDirectory = (0,
|
|
77
|
+
this.datasetDirectory = (0, utils_1.resolveWithinDirectory)(options.baseStorageDirectory, this.name ?? this.id);
|
|
79
78
|
this.client = options.client;
|
|
80
79
|
}
|
|
81
80
|
async get() {
|
|
@@ -108,7 +107,7 @@ class DatasetClient extends base_client_1.BaseClient {
|
|
|
108
107
|
}
|
|
109
108
|
existingStoreById.name = parsed.name;
|
|
110
109
|
const previousDir = existingStoreById.datasetDirectory;
|
|
111
|
-
existingStoreById.datasetDirectory = (0,
|
|
110
|
+
existingStoreById.datasetDirectory = (0, utils_1.resolveWithinDirectory)(this.client.datasetsDirectory, parsed.name ?? existingStoreById.name ?? existingStoreById.id);
|
|
112
111
|
await (0, fs_extra_1.move)(previousDir, existingStoreById.datasetDirectory, { overwrite: true });
|
|
113
112
|
// Update timestamps
|
|
114
113
|
existingStoreById.updateTimestamps(true);
|
|
@@ -4,7 +4,6 @@ exports.KeyValueStoreClient = void 0;
|
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const node_crypto_1 = require("node:crypto");
|
|
6
6
|
const promises_1 = require("node:fs/promises");
|
|
7
|
-
const node_path_1 = require("node:path");
|
|
8
7
|
const node_stream_1 = require("node:stream");
|
|
9
8
|
const shapeshift_1 = require("@sapphire/shapeshift");
|
|
10
9
|
const fs_extra_1 = require("fs-extra");
|
|
@@ -65,7 +64,7 @@ class KeyValueStoreClient extends base_client_1.BaseClient {
|
|
|
65
64
|
value: void 0
|
|
66
65
|
});
|
|
67
66
|
this.name = options.name;
|
|
68
|
-
this.keyValueStoreDirectory = (0,
|
|
67
|
+
this.keyValueStoreDirectory = (0, utils_1.resolveWithinDirectory)(options.baseStorageDirectory, this.name ?? this.id);
|
|
69
68
|
this.client = options.client;
|
|
70
69
|
}
|
|
71
70
|
async get() {
|
|
@@ -98,7 +97,7 @@ class KeyValueStoreClient extends base_client_1.BaseClient {
|
|
|
98
97
|
}
|
|
99
98
|
existingStoreById.name = parsed.name;
|
|
100
99
|
const previousDir = existingStoreById.keyValueStoreDirectory;
|
|
101
|
-
existingStoreById.keyValueStoreDirectory = (0,
|
|
100
|
+
existingStoreById.keyValueStoreDirectory = (0, utils_1.resolveWithinDirectory)(this.client.keyValueStoresDirectory, parsed.name ?? existingStoreById.name ?? existingStoreById.id);
|
|
102
101
|
await (0, fs_extra_1.move)(previousDir, existingStoreById.keyValueStoreDirectory, { overwrite: true });
|
|
103
102
|
// Update timestamps
|
|
104
103
|
existingStoreById.updateTimestamps(true);
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.RequestQueueClient = void 0;
|
|
4
4
|
const node_crypto_1 = require("node:crypto");
|
|
5
5
|
const promises_1 = require("node:fs/promises");
|
|
6
|
-
const node_path_1 = require("node:path");
|
|
7
6
|
const async_queue_1 = require("@sapphire/async-queue");
|
|
8
7
|
const shapeshift_1 = require("@sapphire/shapeshift");
|
|
9
8
|
const fs_extra_1 = require("fs-extra");
|
|
@@ -96,7 +95,7 @@ class RequestQueueClient extends base_client_1.BaseClient {
|
|
|
96
95
|
value: void 0
|
|
97
96
|
});
|
|
98
97
|
this.name = options.name;
|
|
99
|
-
this.requestQueueDirectory = (0,
|
|
98
|
+
this.requestQueueDirectory = (0, utils_1.resolveWithinDirectory)(options.baseStorageDirectory, this.name ?? this.id);
|
|
100
99
|
this.client = options.client;
|
|
101
100
|
}
|
|
102
101
|
async getQueue() {
|
|
@@ -138,7 +137,7 @@ class RequestQueueClient extends base_client_1.BaseClient {
|
|
|
138
137
|
}
|
|
139
138
|
existingQueueById.name = parsed.name;
|
|
140
139
|
const previousDir = existingQueueById.requestQueueDirectory;
|
|
141
|
-
existingQueueById.requestQueueDirectory = (0,
|
|
140
|
+
existingQueueById.requestQueueDirectory = (0, utils_1.resolveWithinDirectory)(this.client.requestQueuesDirectory, parsed.name ?? existingQueueById.name ?? existingQueueById.id);
|
|
142
141
|
await (0, fs_extra_1.move)(previousDir, existingQueueById.requestQueueDirectory, { overwrite: true });
|
|
143
142
|
// Update timestamps
|
|
144
143
|
existingQueueById.updateTimestamps(true);
|
package/utils.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import type * as storage from '@crawlee/types';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves `segment` against `baseDirectory` and ensures the result stays within `baseDirectory`.
|
|
4
|
+
* Storage names and record keys are used as filesystem path components, so a value containing `..`
|
|
5
|
+
* or an absolute path could otherwise escape the intended directory.
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveWithinDirectory(baseDirectory: string, segment: string): string;
|
|
2
8
|
/**
|
|
3
9
|
* Removes all properties with a null value
|
|
4
10
|
* from the provided object.
|
package/utils.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.memoryStorageLog = void 0;
|
|
4
|
+
exports.resolveWithinDirectory = resolveWithinDirectory;
|
|
4
5
|
exports.purgeNullsFromObject = purgeNullsFromObject;
|
|
5
6
|
exports.uniqueKeyToRequestId = uniqueKeyToRequestId;
|
|
6
7
|
exports.isBuffer = isBuffer;
|
|
@@ -12,9 +13,24 @@ exports.createPaginatedEntryList = createPaginatedEntryList;
|
|
|
12
13
|
exports.createLazyIterablePromise = createLazyIterablePromise;
|
|
13
14
|
const tslib_1 = require("tslib");
|
|
14
15
|
const node_crypto_1 = require("node:crypto");
|
|
16
|
+
const node_path_1 = require("node:path");
|
|
15
17
|
const shapeshift_1 = require("@sapphire/shapeshift");
|
|
16
18
|
const log_1 = tslib_1.__importDefault(require("@apify/log"));
|
|
17
19
|
const consts_1 = require("./consts");
|
|
20
|
+
/**
|
|
21
|
+
* Resolves `segment` against `baseDirectory` and ensures the result stays within `baseDirectory`.
|
|
22
|
+
* Storage names and record keys are used as filesystem path components, so a value containing `..`
|
|
23
|
+
* or an absolute path could otherwise escape the intended directory.
|
|
24
|
+
*/
|
|
25
|
+
function resolveWithinDirectory(baseDirectory, segment) {
|
|
26
|
+
const base = (0, node_path_1.resolve)(baseDirectory);
|
|
27
|
+
const resolved = (0, node_path_1.resolve)(base, segment);
|
|
28
|
+
if (resolved !== base && !resolved.startsWith(`${base}${node_path_1.sep}`)) {
|
|
29
|
+
throw new Error(`"${segment}" is not allowed because it would resolve outside of the storage directory. ` +
|
|
30
|
+
`Storage names and record keys must not contain path traversal segments ("..") or absolute paths.`);
|
|
31
|
+
}
|
|
32
|
+
return resolved;
|
|
33
|
+
}
|
|
18
34
|
/**
|
|
19
35
|
* Removes all properties with a null value
|
|
20
36
|
* from the provided object.
|