@tinacms/cli 2.5.1 → 2.5.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.
@@ -14,5 +14,5 @@ export declare const generateCollections: ({ pathToForestryConfig, usingTypescri
14
14
  }) => Promise<{
15
15
  collections: Collection[];
16
16
  importStatements: string;
17
- templateCode: string;
17
+ templateCode: any;
18
18
  }>;
@@ -34,5 +34,5 @@ export declare const makeTemplateFile: ({ templateMap, usingTypescript, }: {
34
34
  usingTypescript: boolean;
35
35
  }) => Promise<{
36
36
  importStatements: string[];
37
- templateCodeText: string;
37
+ templateCodeText: any;
38
38
  }>;
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { Cli, Builtins } from "clipanion";
3
3
 
4
4
  // package.json
5
- var version = "2.5.1";
5
+ var version = "2.5.2";
6
6
 
7
7
  // src/next/commands/dev-command/index.ts
8
8
  import path10 from "path";
@@ -1699,32 +1699,30 @@ function expandOrigins(raw) {
1699
1699
  const filtered = raw.filter((o) => o !== "private");
1700
1700
  return hasPrivate ? [...filtered, PRIVATE_NETWORK_RE] : filtered;
1701
1701
  }
1702
- function buildCorsOriginCheck(allowedOrigins = []) {
1703
- const extra = expandOrigins(allowedOrigins);
1704
- return (origin, callback) => {
1705
- if (!origin) {
1706
- callback(null, true);
1707
- return;
1708
- }
1709
- if (LOCALHOST_RE.test(origin)) {
1710
- callback(null, true);
1711
- return;
1712
- }
1713
- for (const allowed of extra) {
1714
- if (typeof allowed === "string") {
1715
- if (allowed === origin) {
1716
- callback(null, true);
1717
- return;
1718
- }
1719
- } else {
1720
- allowed.lastIndex = 0;
1721
- if (allowed.test(origin)) {
1722
- callback(null, true);
1723
- return;
1724
- }
1702
+ function isOriginAllowed(origin, allowedOrigins = []) {
1703
+ if (!origin) {
1704
+ return true;
1705
+ }
1706
+ if (LOCALHOST_RE.test(origin)) {
1707
+ return true;
1708
+ }
1709
+ for (const allowed of expandOrigins(allowedOrigins)) {
1710
+ if (typeof allowed === "string") {
1711
+ if (allowed === origin) {
1712
+ return true;
1713
+ }
1714
+ } else {
1715
+ allowed.lastIndex = 0;
1716
+ if (allowed.test(origin)) {
1717
+ return true;
1725
1718
  }
1726
1719
  }
1727
- callback(null, false);
1720
+ }
1721
+ return false;
1722
+ }
1723
+ function buildCorsOriginCheck(allowedOrigins = []) {
1724
+ return (origin, callback) => {
1725
+ callback(null, isOriginAllowed(origin, allowedOrigins));
1728
1726
  };
1729
1727
  }
1730
1728
 
@@ -2582,9 +2580,17 @@ var devServerEndPointsPlugin = ({
2582
2580
  searchIndex,
2583
2581
  databaseLock
2584
2582
  }) => {
2585
- const corsOriginCheck = buildCorsOriginCheck(
2586
- configManager.config?.server?.allowedOrigins
2587
- );
2583
+ const allowedOrigins = configManager.config?.server?.allowedOrigins;
2584
+ const corsOriginCheck = buildCorsOriginCheck(allowedOrigins);
2585
+ const isStateChangingRequest = (req) => {
2586
+ const url = req.url || "";
2587
+ if (url.startsWith("/media/upload")) return true;
2588
+ if (url.startsWith("/media") && req.method === "DELETE") return true;
2589
+ if (url.startsWith("/graphql") && req.method === "POST") return true;
2590
+ if ((url.startsWith("/searchIndex") || url.startsWith("/v2/searchIndex")) && (req.method === "POST" || req.method === "DELETE"))
2591
+ return true;
2592
+ return false;
2593
+ };
2588
2594
  const plug = {
2589
2595
  name: "graphql-endpoints",
2590
2596
  configureServer(server) {
@@ -2607,6 +2613,11 @@ var devServerEndPointsPlugin = ({
2607
2613
  config: { apiURL, searchPath: "searchIndex" },
2608
2614
  searchIndex
2609
2615
  });
2616
+ if (isStateChangingRequest(req) && !isOriginAllowed(req.headers.origin, allowedOrigins)) {
2617
+ res.statusCode = 403;
2618
+ res.end(JSON.stringify({ error: "Origin not allowed" }));
2619
+ return;
2620
+ }
2610
2621
  if (req.url.startsWith("/media/upload")) {
2611
2622
  await mediaRouter.handlePost(req, res);
2612
2623
  return;
@@ -8,6 +8,18 @@
8
8
  * bridge networks, etc.
9
9
  */
10
10
  /**
11
- * Build a CORS `origin` callback compatible with the `cors` npm package.
11
+ * Decide whether a request `Origin` is allowed to reach the dev server.
12
+ *
13
+ * No `Origin` (curl, same-origin, CLI tooling) and localhost are always
14
+ * allowed; extra `allowedOrigins` (with `'private'` expanded) match by exact
15
+ * string or RegExp.
16
+ */
17
+ export declare function isOriginAllowed(origin: string | undefined, allowedOrigins?: (string | RegExp)[]): boolean;
18
+ /**
19
+ * Build a CORS `origin` callback for the `cors` npm package.
20
+ *
21
+ * NOTE: `cors` only uses this to set the `Access-Control-Allow-Origin` header;
22
+ * it does NOT reject disallowed requests server-side. State-changing routes
23
+ * must also gate on {@link isOriginAllowed} (see plugins.ts).
12
24
  */
13
25
  export declare function buildCorsOriginCheck(allowedOrigins?: (string | RegExp)[]): (origin: string | undefined, callback: (err: Error | null, allow?: boolean) => void) => void;
package/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "@tinacms/cli",
3
3
  "type": "module",
4
- "version": "2.5.1",
4
+ "version": "2.5.2",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
7
7
  "files": [
8
8
  "dist",
9
- "bin/*",
10
- ".env"
9
+ "bin/*"
11
10
  ],
12
11
  "license": "Apache-2.0",
13
12
  "bin": {
@@ -44,11 +43,11 @@
44
43
  "mongodb-level": "^0.0.4",
45
44
  "sqlite-level": "^2.1.0",
46
45
  "ts-jest": "^29.2.5",
47
- "@tinacms/scripts": "1.6.1"
46
+ "@tinacms/scripts": "1.6.2"
48
47
  },
49
48
  "dependencies": {
50
49
  "@graphql-codegen/core": "^2.6.8",
51
- "@graphql-codegen/plugin-helpers": "latest",
50
+ "@graphql-codegen/plugin-helpers": "^7.0.1",
52
51
  "@graphql-codegen/typescript": "^4.1.3",
53
52
  "@graphql-codegen/typescript-operations": "^4.4.1",
54
53
  "@graphql-codegen/visitor-plugin-common": "^4.1.2",
@@ -92,12 +91,12 @@
92
91
  "vite": "^4.5.9",
93
92
  "yup": "^1.6.1",
94
93
  "zod": "^3.24.2",
95
- "@tinacms/app": "2.5.6",
96
- "@tinacms/graphql": "2.4.5",
97
- "@tinacms/metrics": "2.1.0",
98
- "tinacms": "3.9.3",
99
- "@tinacms/schema-tools": "2.8.1",
100
- "@tinacms/search": "1.2.19"
94
+ "@tinacms/app": "2.5.7",
95
+ "@tinacms/schema-tools": "2.8.2",
96
+ "@tinacms/graphql": "2.4.6",
97
+ "@tinacms/search": "1.2.20",
98
+ "tinacms": "3.9.4",
99
+ "@tinacms/metrics": "2.1.1"
101
100
  },
102
101
  "publishConfig": {
103
102
  "registry": "https://registry.npmjs.org"
@@ -1,4 +0,0 @@
1
- /**
2
-
3
- */
4
- export * from './server';
@@ -1,56 +0,0 @@
1
- /**
2
-
3
- */
4
- interface MediaArgs {
5
- searchPath: string;
6
- cursor?: string;
7
- limit?: string;
8
- }
9
- interface File {
10
- src: string;
11
- filename: string;
12
- size: number;
13
- }
14
- interface ListMediaRes {
15
- directories: string[];
16
- files: File[];
17
- cursor?: string;
18
- error?: string;
19
- }
20
- export interface PathConfig {
21
- rootPath: string;
22
- publicFolder: string;
23
- mediaRoot: string;
24
- }
25
- type SuccessRecord = {
26
- ok: true;
27
- } | {
28
- ok: false;
29
- message: string;
30
- };
31
- /**
32
- * Handles media file operations (list, delete) for the Express-based server.
33
- *
34
- * @security Every method that accepts a user-supplied `searchPath` validates
35
- * it against the media root using `resolveWithinBase` (list) or
36
- * `resolveStrictlyWithinBase` (delete) before any filesystem access.
37
- *
38
- * - **list** uses `resolveWithinBase` because listing the media root itself
39
- * (empty path / exact base match) is a valid operation.
40
- * - **delete** uses `resolveStrictlyWithinBase` because deleting the media
41
- * root directory itself must never be allowed.
42
- *
43
- * Both methods catch `PathTraversalError` and re-throw it so that the
44
- * route handler can return a 403 response. Other errors are caught and
45
- * returned as structured error responses (this avoids leaking stack traces
46
- * to the client).
47
- */
48
- export declare class MediaModel {
49
- readonly rootPath: string;
50
- readonly publicFolder: string;
51
- readonly mediaRoot: string;
52
- constructor({ rootPath, publicFolder, mediaRoot }: PathConfig);
53
- listMedia(args: MediaArgs): Promise<ListMediaRes>;
54
- deleteMedia(args: MediaArgs): Promise<SuccessRecord>;
55
- }
56
- export {};
@@ -1,6 +0,0 @@
1
- /**
2
-
3
- */
4
- import { Router } from 'express';
5
- import { PathConfig } from '../models/media';
6
- export declare const createMediaRouter: (config: PathConfig) => Router;
@@ -1,5 +0,0 @@
1
- /**
2
-
3
- */
4
- import http from 'node:http';
5
- export declare const gqlServer: (database: any, verbose: boolean) => Promise<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>>;