@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 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.0",
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
- const normalizedPath = path3.normalize(fullPath);
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 pathSegments = [collection.path, relativePath];
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 ? relativePath : void 0
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, filepath));
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
- const qualified = relativePath ? `${relativePath}/${filepath}` : filepath;
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(
@@ -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.0",
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.7",
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.5.0"
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
  }