@tinacms/graphql 2.2.0 → 2.2.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 +49 -9
- package/dist/resolver/index.d.ts +1 -0
- package/package.json +5 -4
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.2.
|
|
3029
|
+
version: "2.2.2",
|
|
3030
3030
|
main: "dist/index.js",
|
|
3031
3031
|
module: "./dist/index.js",
|
|
3032
3032
|
files: [
|
|
@@ -3053,7 +3053,7 @@ var package_default = {
|
|
|
3053
3053
|
types: "pnpm tsc",
|
|
3054
3054
|
build: "tinacms-scripts build",
|
|
3055
3055
|
docs: "pnpm typedoc",
|
|
3056
|
-
test: "vitest run",
|
|
3056
|
+
test: "vitest run --coverage.enabled",
|
|
3057
3057
|
"test-watch": "vitest"
|
|
3058
3058
|
},
|
|
3059
3059
|
dependencies: {
|
|
@@ -3099,6 +3099,7 @@ var package_default = {
|
|
|
3099
3099
|
"@types/node": "^22.13.1",
|
|
3100
3100
|
"@types/normalize-path": "catalog:",
|
|
3101
3101
|
"@types/ws": "catalog:",
|
|
3102
|
+
"@vitest/coverage-v8": "0.32.4",
|
|
3102
3103
|
"jest-file-snapshot": "^0.5.0",
|
|
3103
3104
|
"memory-level": "catalog:",
|
|
3104
3105
|
typescript: "^5.7.3",
|
|
@@ -4916,7 +4917,7 @@ var updateObjectWithJsonPath = (obj, path9, oldValue, newValue) => {
|
|
|
4916
4917
|
}
|
|
4917
4918
|
return { object: obj, updated };
|
|
4918
4919
|
};
|
|
4919
|
-
var Resolver = class {
|
|
4920
|
+
var Resolver = class _Resolver {
|
|
4920
4921
|
constructor(init) {
|
|
4921
4922
|
this.init = init;
|
|
4922
4923
|
this.config = init.config;
|
|
@@ -5187,8 +5188,17 @@ var Resolver = class {
|
|
|
5187
5188
|
* path traversal attacks where a user might attempt to read or write files
|
|
5188
5189
|
* outside of the intended collection.
|
|
5189
5190
|
*/
|
|
5191
|
+
static sanitizePath(input) {
|
|
5192
|
+
if (input.includes("\0")) {
|
|
5193
|
+
throw new Error("Invalid path: null bytes are not allowed");
|
|
5194
|
+
}
|
|
5195
|
+
return input.replace(/\\/g, "/");
|
|
5196
|
+
}
|
|
5190
5197
|
validatePath = (fullPath, collection, relativePath) => {
|
|
5191
|
-
|
|
5198
|
+
if (fullPath.includes("\0")) {
|
|
5199
|
+
throw new Error("Invalid path: null bytes are not allowed");
|
|
5200
|
+
}
|
|
5201
|
+
const normalizedPath = path3.normalize(fullPath.replace(/\\/g, "/"));
|
|
5192
5202
|
const normalizedCollectionPath = path3.normalize(collection.path);
|
|
5193
5203
|
const relative = path3.relative(normalizedCollectionPath, normalizedPath);
|
|
5194
5204
|
if (relative.startsWith("..")) {
|
|
@@ -5219,16 +5229,17 @@ var Resolver = class {
|
|
|
5219
5229
|
*/
|
|
5220
5230
|
getValidatedPath = (collectionName, relativePath, options) => {
|
|
5221
5231
|
const collection = this.getCollectionWithName(collectionName);
|
|
5222
|
-
const
|
|
5232
|
+
const sanitizedRelativePath = _Resolver.sanitizePath(relativePath);
|
|
5233
|
+
const pathSegments = [collection.path, sanitizedRelativePath];
|
|
5223
5234
|
if (options?.extraSegments) {
|
|
5224
|
-
pathSegments.push(...options.extraSegments);
|
|
5235
|
+
pathSegments.push(...options.extraSegments.map(_Resolver.sanitizePath));
|
|
5225
5236
|
}
|
|
5226
5237
|
const realPath = path3.join(...pathSegments);
|
|
5227
5238
|
const shouldValidateExtension = options?.validateExtension !== false;
|
|
5228
5239
|
this.validatePath(
|
|
5229
5240
|
realPath,
|
|
5230
5241
|
collection,
|
|
5231
|
-
shouldValidateExtension ?
|
|
5242
|
+
shouldValidateExtension ? sanitizedRelativePath : void 0
|
|
5232
5243
|
);
|
|
5233
5244
|
return { collection, realPath };
|
|
5234
5245
|
};
|
|
@@ -8020,14 +8031,39 @@ import path7 from "path";
|
|
|
8020
8031
|
import fg from "fast-glob";
|
|
8021
8032
|
import fs2 from "fs-extra";
|
|
8022
8033
|
import normalize from "normalize-path";
|
|
8034
|
+
function resolveRealPath(candidate) {
|
|
8035
|
+
try {
|
|
8036
|
+
return fs2.realpathSync(candidate);
|
|
8037
|
+
} catch {
|
|
8038
|
+
const parent = path7.dirname(candidate);
|
|
8039
|
+
if (parent === candidate) return candidate;
|
|
8040
|
+
return path7.join(resolveRealPath(parent), path7.basename(candidate));
|
|
8041
|
+
}
|
|
8042
|
+
}
|
|
8023
8043
|
function assertWithinBase(filepath, baseDir) {
|
|
8044
|
+
if (filepath.includes("\0")) {
|
|
8045
|
+
throw new Error("Invalid path: null bytes are not allowed");
|
|
8046
|
+
}
|
|
8047
|
+
const sanitized = filepath.replace(/\\/g, "/");
|
|
8024
8048
|
const resolvedBase = path7.resolve(baseDir);
|
|
8025
|
-
const resolved = path7.resolve(path7.join(baseDir,
|
|
8049
|
+
const resolved = path7.resolve(path7.join(baseDir, sanitized));
|
|
8026
8050
|
if (resolved !== resolvedBase && !resolved.startsWith(resolvedBase + path7.sep)) {
|
|
8027
8051
|
throw new Error(
|
|
8028
8052
|
`Path traversal detected: "${filepath}" escapes the base directory`
|
|
8029
8053
|
);
|
|
8030
8054
|
}
|
|
8055
|
+
try {
|
|
8056
|
+
const realBase = fs2.realpathSync(resolvedBase);
|
|
8057
|
+
const realResolved = resolveRealPath(resolved);
|
|
8058
|
+
if (realResolved !== realBase && !realResolved.startsWith(realBase + path7.sep)) {
|
|
8059
|
+
throw new Error(
|
|
8060
|
+
`Path traversal detected: "${filepath}" escapes the base directory`
|
|
8061
|
+
);
|
|
8062
|
+
}
|
|
8063
|
+
} catch (err) {
|
|
8064
|
+
if (err instanceof Error && err.message.startsWith("Path traversal"))
|
|
8065
|
+
throw err;
|
|
8066
|
+
}
|
|
8031
8067
|
return resolved;
|
|
8032
8068
|
}
|
|
8033
8069
|
var FilesystemBridge = class {
|
|
@@ -8089,7 +8125,11 @@ import { GraphQLError as GraphQLError6 } from "graphql";
|
|
|
8089
8125
|
import git2 from "isomorphic-git";
|
|
8090
8126
|
import normalize2 from "normalize-path";
|
|
8091
8127
|
function assertWithinBase2(filepath, relativePath) {
|
|
8092
|
-
|
|
8128
|
+
if (filepath.includes("\0")) {
|
|
8129
|
+
throw new Error("Invalid path: null bytes are not allowed");
|
|
8130
|
+
}
|
|
8131
|
+
const sanitized = filepath.replace(/\\/g, "/");
|
|
8132
|
+
const qualified = relativePath ? `${relativePath}/${sanitized}` : sanitized;
|
|
8093
8133
|
const normalized = path8.normalize(qualified);
|
|
8094
8134
|
if (normalized.startsWith("..") || normalized.startsWith("/") || path8.isAbsolute(normalized) || relativePath && normalized !== relativePath && !normalized.startsWith(relativePath + "/")) {
|
|
8095
8135
|
throw new Error(
|
package/dist/resolver/index.d.ts
CHANGED
|
@@ -235,6 +235,7 @@ export declare class Resolver {
|
|
|
235
235
|
* path traversal attacks where a user might attempt to read or write files
|
|
236
236
|
* outside of the intended collection.
|
|
237
237
|
*/
|
|
238
|
+
private static sanitizePath;
|
|
238
239
|
private validatePath;
|
|
239
240
|
/**
|
|
240
241
|
* Helper method to get collection and construct validated path.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tinacms/graphql",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.2.
|
|
4
|
+
"version": "2.2.2",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
7
7
|
"files": [
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"normalize-path": "^3.0.0",
|
|
44
44
|
"readable-stream": "^4.7.0",
|
|
45
45
|
"yup": "^1.6.1",
|
|
46
|
-
"@tinacms/mdx": "2.0
|
|
46
|
+
"@tinacms/mdx": "2.1.0",
|
|
47
47
|
"@tinacms/schema-tools": "2.7.0"
|
|
48
48
|
},
|
|
49
49
|
"publishConfig": {
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
"@types/node": "^22.13.1",
|
|
66
66
|
"@types/normalize-path": "^3.0.2",
|
|
67
67
|
"@types/ws": "^7.4.7",
|
|
68
|
+
"@vitest/coverage-v8": "0.32.4",
|
|
68
69
|
"jest-file-snapshot": "^0.5.0",
|
|
69
70
|
"memory-level": "^1.0.0",
|
|
70
71
|
"typescript": "^5.7.3",
|
|
@@ -72,13 +73,13 @@
|
|
|
72
73
|
"vitest": "^0.32.4",
|
|
73
74
|
"zod": "^3.24.2",
|
|
74
75
|
"@tinacms/schema-tools": "2.7.0",
|
|
75
|
-
"@tinacms/scripts": "1.
|
|
76
|
+
"@tinacms/scripts": "1.6.0"
|
|
76
77
|
},
|
|
77
78
|
"scripts": {
|
|
78
79
|
"types": "pnpm tsc",
|
|
79
80
|
"build": "tinacms-scripts build",
|
|
80
81
|
"docs": "pnpm typedoc",
|
|
81
|
-
"test": "vitest run",
|
|
82
|
+
"test": "vitest run --coverage.enabled",
|
|
82
83
|
"test-watch": "vitest"
|
|
83
84
|
}
|
|
84
85
|
}
|