@tinacms/graphql 2.1.1 → 2.1.2
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/dist/index.js +86 -14
- package/dist/resolver/index.d.ts +19 -0
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -3026,7 +3026,7 @@ var validateField = async (field) => {
|
|
|
3026
3026
|
var package_default = {
|
|
3027
3027
|
name: "@tinacms/graphql",
|
|
3028
3028
|
type: "module",
|
|
3029
|
-
version: "2.1.
|
|
3029
|
+
version: "2.1.2",
|
|
3030
3030
|
main: "dist/index.js",
|
|
3031
3031
|
module: "./dist/index.js",
|
|
3032
3032
|
files: [
|
|
@@ -5100,8 +5100,10 @@ var Resolver = class {
|
|
|
5100
5100
|
relativePath,
|
|
5101
5101
|
templateName
|
|
5102
5102
|
}) => {
|
|
5103
|
-
const collection = this.
|
|
5104
|
-
|
|
5103
|
+
const { collection, realPath } = this.getValidatedPath(
|
|
5104
|
+
collectionName,
|
|
5105
|
+
relativePath
|
|
5106
|
+
);
|
|
5105
5107
|
const alreadyExists = await this.database.documentExists(realPath);
|
|
5106
5108
|
if (alreadyExists) {
|
|
5107
5109
|
throw new Error(`Unable to add document, ${realPath} already exists`);
|
|
@@ -5178,6 +5180,58 @@ var Resolver = class {
|
|
|
5178
5180
|
}
|
|
5179
5181
|
return this.tinaSchema.getCollection(collectionName);
|
|
5180
5182
|
};
|
|
5183
|
+
/**
|
|
5184
|
+
* validatePath ensures that the provided path remains within the boundaries
|
|
5185
|
+
* of the collection's directory and that the file extension matches the
|
|
5186
|
+
* collection's configured format. This is a critical security check to prevent
|
|
5187
|
+
* path traversal attacks where a user might attempt to read or write files
|
|
5188
|
+
* outside of the intended collection.
|
|
5189
|
+
*/
|
|
5190
|
+
validatePath = (fullPath, collection, relativePath) => {
|
|
5191
|
+
const normalizedPath = path3.normalize(fullPath);
|
|
5192
|
+
const normalizedCollectionPath = path3.normalize(collection.path);
|
|
5193
|
+
const relative = path3.relative(normalizedCollectionPath, normalizedPath);
|
|
5194
|
+
if (relative.startsWith("..")) {
|
|
5195
|
+
throw new Error(`Invalid path: path escapes the collection directory`);
|
|
5196
|
+
}
|
|
5197
|
+
if (path3.isAbsolute(relative)) {
|
|
5198
|
+
throw new Error(`Invalid path: absolute paths are not allowed`);
|
|
5199
|
+
}
|
|
5200
|
+
if (relativePath) {
|
|
5201
|
+
const collectionFormat = collection.format || "md";
|
|
5202
|
+
const fileExtension = path3.extname(relativePath).toLowerCase().slice(1);
|
|
5203
|
+
if (fileExtension !== collectionFormat) {
|
|
5204
|
+
throw new Error(
|
|
5205
|
+
`Invalid file extension: expected '.${collectionFormat}' but got '.${fileExtension}'`
|
|
5206
|
+
);
|
|
5207
|
+
}
|
|
5208
|
+
}
|
|
5209
|
+
};
|
|
5210
|
+
/**
|
|
5211
|
+
* Helper method to get collection and construct validated path.
|
|
5212
|
+
* This encapsulates the common pattern of getting a collection, joining paths,
|
|
5213
|
+
* and validating the result, ensuring security checks are always performed.
|
|
5214
|
+
*
|
|
5215
|
+
* @param collectionName - Name of the collection
|
|
5216
|
+
* @param relativePath - Relative path within the collection
|
|
5217
|
+
* @param options - Optional configuration
|
|
5218
|
+
* @returns Object containing the collection and validated real path
|
|
5219
|
+
*/
|
|
5220
|
+
getValidatedPath = (collectionName, relativePath, options) => {
|
|
5221
|
+
const collection = this.getCollectionWithName(collectionName);
|
|
5222
|
+
const pathSegments = [collection.path, relativePath];
|
|
5223
|
+
if (options?.extraSegments) {
|
|
5224
|
+
pathSegments.push(...options.extraSegments);
|
|
5225
|
+
}
|
|
5226
|
+
const realPath = path3.join(...pathSegments);
|
|
5227
|
+
const shouldValidateExtension = options?.validateExtension !== false;
|
|
5228
|
+
this.validatePath(
|
|
5229
|
+
realPath,
|
|
5230
|
+
collection,
|
|
5231
|
+
shouldValidateExtension ? relativePath : void 0
|
|
5232
|
+
);
|
|
5233
|
+
return { collection, realPath };
|
|
5234
|
+
};
|
|
5181
5235
|
/*
|
|
5182
5236
|
* Used for getDocument, get<Collection>Document.
|
|
5183
5237
|
*/
|
|
@@ -5185,8 +5239,10 @@ var Resolver = class {
|
|
|
5185
5239
|
collectionName,
|
|
5186
5240
|
relativePath
|
|
5187
5241
|
}) => {
|
|
5188
|
-
const collection = this.
|
|
5189
|
-
|
|
5242
|
+
const { collection, realPath } = this.getValidatedPath(
|
|
5243
|
+
collectionName,
|
|
5244
|
+
relativePath
|
|
5245
|
+
);
|
|
5190
5246
|
return this.getDocument(realPath, {
|
|
5191
5247
|
collection,
|
|
5192
5248
|
checkReferences: true
|
|
@@ -5205,6 +5261,7 @@ var Resolver = class {
|
|
|
5205
5261
|
relativePath,
|
|
5206
5262
|
`.gitkeep.${collection.format || "md"}`
|
|
5207
5263
|
);
|
|
5264
|
+
this.validatePath(realPath, collection);
|
|
5208
5265
|
const alreadyExists = await this.database.documentExists(realPath);
|
|
5209
5266
|
if (alreadyExists) {
|
|
5210
5267
|
throw new Error(`Unable to add folder, ${realPath} already exists`);
|
|
@@ -5221,8 +5278,10 @@ var Resolver = class {
|
|
|
5221
5278
|
relativePath,
|
|
5222
5279
|
body
|
|
5223
5280
|
}) => {
|
|
5224
|
-
const collection = this.
|
|
5225
|
-
|
|
5281
|
+
const { collection, realPath } = this.getValidatedPath(
|
|
5282
|
+
collectionName,
|
|
5283
|
+
relativePath
|
|
5284
|
+
);
|
|
5226
5285
|
const alreadyExists = await this.database.documentExists(realPath);
|
|
5227
5286
|
if (alreadyExists) {
|
|
5228
5287
|
throw new Error(`Unable to add document, ${realPath} already exists`);
|
|
@@ -5237,15 +5296,20 @@ var Resolver = class {
|
|
|
5237
5296
|
newRelativePath,
|
|
5238
5297
|
newBody
|
|
5239
5298
|
}) => {
|
|
5240
|
-
const collection = this.
|
|
5241
|
-
|
|
5299
|
+
const { collection, realPath } = this.getValidatedPath(
|
|
5300
|
+
collectionName,
|
|
5301
|
+
relativePath
|
|
5302
|
+
);
|
|
5242
5303
|
const alreadyExists = await this.database.documentExists(realPath);
|
|
5243
5304
|
if (!alreadyExists) {
|
|
5244
5305
|
throw new Error(`Unable to update document, ${realPath} does not exist`);
|
|
5245
5306
|
}
|
|
5246
5307
|
const doc = await this.getDocument(realPath);
|
|
5247
5308
|
if (newRelativePath) {
|
|
5248
|
-
const newRealPath =
|
|
5309
|
+
const { realPath: newRealPath } = this.getValidatedPath(
|
|
5310
|
+
collectionName,
|
|
5311
|
+
newRelativePath
|
|
5312
|
+
);
|
|
5249
5313
|
if (newRealPath === realPath) {
|
|
5250
5314
|
return doc;
|
|
5251
5315
|
}
|
|
@@ -5308,8 +5372,10 @@ var Resolver = class {
|
|
|
5308
5372
|
collectionName,
|
|
5309
5373
|
relativePath
|
|
5310
5374
|
}) => {
|
|
5311
|
-
const collection = this.
|
|
5312
|
-
|
|
5375
|
+
const { collection, realPath } = this.getValidatedPath(
|
|
5376
|
+
collectionName,
|
|
5377
|
+
relativePath
|
|
5378
|
+
);
|
|
5313
5379
|
const alreadyExists = await this.database.documentExists(realPath);
|
|
5314
5380
|
if (!alreadyExists) {
|
|
5315
5381
|
throw new Error(`Unable to delete document, ${realPath} does not exist`);
|
|
@@ -5528,7 +5594,10 @@ var Resolver = class {
|
|
|
5528
5594
|
params: yup3.object({ relativePath: yup3.string().required() }).required()
|
|
5529
5595
|
})
|
|
5530
5596
|
);
|
|
5531
|
-
const realPath =
|
|
5597
|
+
const realPath = this.getValidatedPath(
|
|
5598
|
+
collection.name,
|
|
5599
|
+
args.relativePath
|
|
5600
|
+
).realPath;
|
|
5532
5601
|
return this.updateResolveDocument({
|
|
5533
5602
|
collection,
|
|
5534
5603
|
realPath,
|
|
@@ -5548,7 +5617,10 @@ var Resolver = class {
|
|
|
5548
5617
|
});
|
|
5549
5618
|
}
|
|
5550
5619
|
} else {
|
|
5551
|
-
const realPath =
|
|
5620
|
+
const realPath = this.getValidatedPath(
|
|
5621
|
+
collection.name,
|
|
5622
|
+
args.relativePath
|
|
5623
|
+
).realPath;
|
|
5552
5624
|
return this.getDocument(realPath, {
|
|
5553
5625
|
collection,
|
|
5554
5626
|
checkReferences: true
|
package/dist/resolver/index.d.ts
CHANGED
|
@@ -228,6 +228,25 @@ export declare class Resolver {
|
|
|
228
228
|
*/
|
|
229
229
|
resolveLegacyValues: (oldDoc: any, collection: Collection<true>) => {};
|
|
230
230
|
private getCollectionWithName;
|
|
231
|
+
/**
|
|
232
|
+
* validatePath ensures that the provided path remains within the boundaries
|
|
233
|
+
* of the collection's directory and that the file extension matches the
|
|
234
|
+
* collection's configured format. This is a critical security check to prevent
|
|
235
|
+
* path traversal attacks where a user might attempt to read or write files
|
|
236
|
+
* outside of the intended collection.
|
|
237
|
+
*/
|
|
238
|
+
private validatePath;
|
|
239
|
+
/**
|
|
240
|
+
* Helper method to get collection and construct validated path.
|
|
241
|
+
* This encapsulates the common pattern of getting a collection, joining paths,
|
|
242
|
+
* and validating the result, ensuring security checks are always performed.
|
|
243
|
+
*
|
|
244
|
+
* @param collectionName - Name of the collection
|
|
245
|
+
* @param relativePath - Relative path within the collection
|
|
246
|
+
* @param options - Optional configuration
|
|
247
|
+
* @returns Object containing the collection and validated real path
|
|
248
|
+
*/
|
|
249
|
+
private getValidatedPath;
|
|
231
250
|
resolveRetrievedDocument: ({ collectionName, relativePath, }: {
|
|
232
251
|
collectionName: string;
|
|
233
252
|
relativePath: string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tinacms/graphql",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.1.
|
|
4
|
+
"version": "2.1.2",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
7
7
|
"files": [
|
|
@@ -37,14 +37,14 @@
|
|
|
37
37
|
"isomorphic-git": "^1.29.0",
|
|
38
38
|
"js-sha1": "^0.6.0",
|
|
39
39
|
"js-yaml": "^3.14.1",
|
|
40
|
-
"jsonpath-plus": "10.
|
|
40
|
+
"jsonpath-plus": "^10.3.0",
|
|
41
41
|
"many-level": "^2.0.0",
|
|
42
42
|
"micromatch": "4.0.8",
|
|
43
43
|
"normalize-path": "^3.0.0",
|
|
44
44
|
"readable-stream": "^4.7.0",
|
|
45
45
|
"yup": "^1.6.1",
|
|
46
|
-
"@tinacms/
|
|
47
|
-
"@tinacms/
|
|
46
|
+
"@tinacms/schema-tools": "2.6.0",
|
|
47
|
+
"@tinacms/mdx": "2.0.6"
|
|
48
48
|
},
|
|
49
49
|
"publishConfig": {
|
|
50
50
|
"registry": "https://registry.npmjs.org"
|
|
@@ -71,8 +71,8 @@
|
|
|
71
71
|
"vite": "^4.5.9",
|
|
72
72
|
"vitest": "^0.32.4",
|
|
73
73
|
"zod": "^3.24.2",
|
|
74
|
-
"@tinacms/
|
|
75
|
-
"@tinacms/
|
|
74
|
+
"@tinacms/schema-tools": "2.6.0",
|
|
75
|
+
"@tinacms/scripts": "1.5.0"
|
|
76
76
|
},
|
|
77
77
|
"scripts": {
|
|
78
78
|
"types": "pnpm tsc",
|