VYPR
High severity8.8NVD Advisory· Published Dec 18, 2025· Updated Apr 10, 2026

CVE-2025-68278

CVE-2025-68278

Description

Tina is a headless content management system. In tinacms prior to version 3.1.1, tinacms uses the gray-matter package in an insecure way allowing attackers that can control the content of the processed markdown files, e.g., blog posts, to execute arbitrary code. tinacms version 3.1.1, @tinacms/cli version 2.0.4, and @tinacms/graphql version 2.0.3 contain a fix for the issue.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
tinacmsnpm
< 3.1.13.1.1
@tinacms/clinpm
< 2.0.42.0.4
@tinacms/graphqlnpm
< 2.0.32.0.3

Affected products

3
  • cpe:2.3:a:ssw:tinacms:*:*:*:*:*:node.js:*:*
    Range: <3.1.1
  • cpe:2.3:a:ssw:tinacms\/cli:*:*:*:*:*:node.js:*:*
    Range: <2.0.4
  • cpe:2.3:a:ssw:tinacms\/graphql:*:*:*:*:*:node.js:*:*
    Range: <2.0.3

Patches

1
fa7c27abef96

fix: Prevent JavaScript and CoffeeScript execution in frontmatter (#6268)

https://github.com/tinacms/tinacmsMatt Wicks [SSW]Dec 17, 2025via ghsa
3 files changed · +353 0
  • .changeset/five-plums-tie.md+11 0 added
    @@ -0,0 +1,11 @@
    +---
    +"@tinacms/graphql": patch
    +---
    +
    +fix: Security patch to prevent arbitrary code execution in markdown frontmatter
    +
    +This fixes a security vulnerability where the `gray-matter` package executes JavaScript code by default when processing markdown files with `---js`, `---javascript`, or `---coffee` frontmatter delimiters. Attackers who could control markdown file content could exploit this to execute arbitrary code on the server.
    +
    +The fix disables JavaScript and CoffeeScript engines in frontmatter parsing. Valid frontmatter formats (YAML, TOML, JSON) continue to work normally, including storing JSX/HTML as string data.
    +
    +**Breaking Change:** If your content files use `---js`, `---javascript`, or `---coffee` frontmatter delimiters, they will now throw an error. You should migrate these files to use YAML, TOML, or JSON frontmatter instead
    \ No newline at end of file
    
  • packages/@tinacms/graphql/src/database/util.test.ts+292 0 added
    @@ -0,0 +1,292 @@
    +import { describe, it, expect } from 'vitest';
    +import { parseFile, stringifyFile } from './util';
    +
    +describe('gray-matter security', () => {
    +  describe('parseFile', () => {
    +    it('should reject JavaScript frontmatter to prevent code execution', () => {
    +      const maliciousContent = `---js
    +{
    +  "title": "Pawned" + console.log(require("fs").readFileSync("/etc/passwd").toString())
    +}
    +---
    +# Blog Post
    +
    +Content here`;
    +
    +      expect(() => {
    +        parseFile(maliciousContent, '.md', (yup) => yup.object({}));
    +      }).toThrow(
    +        'JavaScript execution in frontmatter is not allowed for security reasons'
    +      );
    +    });
    +
    +    it('should reject javascript (alternate syntax) frontmatter', () => {
    +      const maliciousContent = `---javascript
    +{
    +  "title": "Pawned" + console.log(require("fs").readFileSync("/etc/passwd").toString())
    +}
    +---
    +# Blog Post
    +
    +Content here`;
    +
    +      expect(() => {
    +        parseFile(maliciousContent, '.md', (yup) => yup.object({}));
    +      }).toThrow(
    +        'JavaScript execution in frontmatter is not allowed for security reasons'
    +      );
    +    });
    +
    +    it('should reject CoffeeScript frontmatter to prevent code execution', () => {
    +      const maliciousContent = `---coffee
    +title: "Pawned" + console.log(require("fs").readFileSync("/etc/passwd").toString())
    +---
    +# Blog Post
    +
    +Content here`;
    +
    +      expect(() => {
    +        parseFile(maliciousContent, '.md', (yup) => yup.object({}));
    +      }).toThrow(
    +        'CoffeeScript execution in frontmatter is not allowed for security reasons'
    +      );
    +    });
    +
    +    it('should reject CoffeeScript (full name) frontmatter to prevent code execution', () => {
    +      const maliciousContent = `---coffeescript
    +title: "Pawned" + console.log(require("fs").readFileSync("/etc/passwd").toString())
    +---
    +# Blog Post
    +
    +Content here`;
    +
    +      expect(() => {
    +        parseFile(maliciousContent, '.md', (yup) => yup.object({}));
    +      }).toThrow(
    +        'CoffeeScript execution in frontmatter is not allowed for security reasons'
    +      );
    +    });
    +
    +    it('should successfully parse YAML frontmatter (default safe format)', () => {
    +      const safeContent = `---
    +title: Safe Title
    +author: Test Author
    +---
    +# Blog Post
    +
    +Content here`;
    +
    +      const result = parseFile(safeContent, '.md', (yup) => yup.object({}));
    +
    +      expect(result).toEqual({
    +        title: 'Safe Title',
    +        author: 'Test Author',
    +        $_body: '# Blog Post\n\nContent here',
    +      });
    +    });
    +
    +    it('should successfully parse TOML frontmatter', () => {
    +      const tomlContent = `+++
    +title = "TOML Title"
    +author = "Test Author"
    ++++
    +# Blog Post
    +
    +Content here`;
    +
    +      const result = parseFile(tomlContent, '.md', (yup) => yup.object({}), {
    +        frontmatterFormat: 'toml',
    +        frontmatterDelimiters: '+++',
    +      });
    +
    +      expect(result).toEqual({
    +        title: 'TOML Title',
    +        author: 'Test Author',
    +        $_body: '# Blog Post\n\nContent here',
    +      });
    +    });
    +
    +    it('should successfully parse JSON frontmatter', () => {
    +      const jsonContent = `---
    +{
    +  "title": "JSON Title",
    +  "author": "Test Author"
    +}
    +---
    +# Blog Post
    +
    +Content here`;
    +
    +      const result = parseFile(jsonContent, '.md', (yup) => yup.object({}), {
    +        frontmatterFormat: 'json',
    +      });
    +
    +      expect(result).toEqual({
    +        title: 'JSON Title',
    +        author: 'Test Author',
    +        $_body: '# Blog Post\n\nContent here',
    +      });
    +    });
    +
    +    it('should successfully parse YAML frontmatter with JSX components as data', () => {
    +      // JSX components stored as string data in YAML should work fine
    +      // This is different from executing JavaScript code
    +      const jsxContent = `---
    +title: Blog with Hero Component
    +hero:
    +  component: '<Hero title="Welcome" subtitle="To our site" />'
    +  props:
    +    variant: primary
    +    showImage: true
    +customComponent: '<CustomSection><p>Content here</p></CustomSection>'
    +---
    +# Blog Post
    +
    +Content with components stored in frontmatter`;
    +
    +      const result = parseFile(jsxContent, '.md', (yup) => yup.object({}));
    +
    +      expect(result).toEqual({
    +        title: 'Blog with Hero Component',
    +        hero: {
    +          component: '<Hero title="Welcome" subtitle="To our site" />',
    +          props: {
    +            variant: 'primary',
    +            showImage: true,
    +          },
    +        },
    +        customComponent: '<CustomSection><p>Content here</p></CustomSection>',
    +        $_body: '# Blog Post\n\nContent with components stored in frontmatter',
    +      });
    +
    +      // Verify the JSX is stored as a string, not executed
    +      expect(typeof result.hero.component).toBe('string');
    +      expect(typeof result.customComponent).toBe('string');
    +    });
    +  });
    +
    +  describe('stringifyFile', () => {
    +    it('should not allow stringify with js engine', () => {
    +      const content = {
    +        title: 'Test',
    +        _relativePath: 'test.md',
    +        _id: 'test.md',
    +        _template: 'post',
    +        _collection: 'posts',
    +        $_body: 'Content',
    +      };
    +
    +      expect(() => {
    +        // @ts-ignore - testing invalid configuration
    +        stringifyFile(content, '.md', false, {
    +          frontmatterFormat: 'js' as any,
    +        });
    +      }).toThrow(
    +        'JavaScript execution in frontmatter is not allowed for security reasons'
    +      );
    +    });
    +
    +    it('should not allow stringify with javascript engine', () => {
    +      const content = {
    +        title: 'Test',
    +        _relativePath: 'test.md',
    +        _id: 'test.md',
    +        _template: 'post',
    +        _collection: 'posts',
    +        $_body: 'Content',
    +      };
    +
    +      expect(() => {
    +        // @ts-ignore - testing invalid configuration
    +        stringifyFile(content, '.md', false, {
    +          frontmatterFormat: 'javascript' as any,
    +        });
    +      }).toThrow(
    +        'JavaScript execution in frontmatter is not allowed for security reasons'
    +      );
    +    });
    +
    +    it('should not allow stringify with coffee engine', () => {
    +      const content = {
    +        title: 'Test',
    +        _relativePath: 'test.md',
    +        _id: 'test.md',
    +        _template: 'post',
    +        _collection: 'posts',
    +        $_body: 'Content',
    +      };
    +
    +      expect(() => {
    +        // @ts-ignore - testing invalid configuration
    +        stringifyFile(content, '.md', false, {
    +          frontmatterFormat: 'coffee' as any,
    +        });
    +      }).toThrow(
    +        'CoffeeScript execution in frontmatter is not allowed for security reasons'
    +      );
    +    });
    +
    +    it('should not allow stringify with coffeescript engine', () => {
    +      const content = {
    +        title: 'Test',
    +        _relativePath: 'test.md',
    +        _id: 'test.md',
    +        _template: 'post',
    +        _collection: 'posts',
    +        $_body: 'Content',
    +      };
    +
    +      expect(() => {
    +        // @ts-ignore - testing invalid configuration
    +        stringifyFile(content, '.md', false, {
    +          frontmatterFormat: 'coffeescript' as any,
    +        });
    +      }).toThrow(
    +        'CoffeeScript execution in frontmatter is not allowed for security reasons'
    +      );
    +    });
    +
    +    it('should successfully stringify with YAML (default)', () => {
    +      const content = {
    +        title: 'Test Title',
    +        author: 'Test Author',
    +        _relativePath: 'test.md',
    +        _id: 'test.md',
    +        _template: 'post',
    +        _collection: 'posts',
    +        $_body: 'Content here',
    +      };
    +
    +      const result = stringifyFile(content, '.md', false);
    +
    +      expect(result).toContain('title: Test Title');
    +      expect(result).toContain('author: Test Author');
    +      expect(result).toContain('Content here');
    +      expect(result).not.toContain('_relativePath');
    +      expect(result).not.toContain('_id');
    +      expect(result).not.toContain('_template');
    +      expect(result).not.toContain('_collection');
    +    });
    +
    +    it('should successfully stringify with TOML', () => {
    +      const content = {
    +        title: 'Test Title',
    +        author: 'Test Author',
    +        _relativePath: 'test.md',
    +        _id: 'test.md',
    +        _template: 'post',
    +        _collection: 'posts',
    +        $_body: 'Content here',
    +      };
    +
    +      const result = stringifyFile(content, '.md', false, {
    +        frontmatterFormat: 'toml',
    +        frontmatterDelimiters: '+++',
    +      });
    +
    +      expect(result).toContain('title = "Test Title"');
    +      expect(result).toContain('author = "Test Author"');
    +      expect(result).toContain('Content here');
    +    });
    +  });
    +});
    
  • packages/@tinacms/graphql/src/database/util.ts+50 0 modified
    @@ -23,6 +23,56 @@ const matterEngines = {
         parse: (val) => toml.parse(val),
         stringify: (val) => toml.stringify(val),
       },
    +  // Disable JavaScript and CoffeeScript execution to prevent code execution vulnerability (CVE)
    +  // gray-matter executes JS/Coffee code by default, which is a security risk
    +  js: {
    +    parse: () => {
    +      throw new Error(
    +        'JavaScript execution in frontmatter is not allowed for security reasons'
    +      );
    +    },
    +    stringify: () => {
    +      throw new Error(
    +        'JavaScript execution in frontmatter is not allowed for security reasons'
    +      );
    +    },
    +  },
    +  javascript: {
    +    parse: () => {
    +      throw new Error(
    +        'JavaScript execution in frontmatter is not allowed for security reasons'
    +      );
    +    },
    +    stringify: () => {
    +      throw new Error(
    +        'JavaScript execution in frontmatter is not allowed for security reasons'
    +      );
    +    },
    +  },
    +  coffee: {
    +    parse: () => {
    +      throw new Error(
    +        'CoffeeScript execution in frontmatter is not allowed for security reasons'
    +      );
    +    },
    +    stringify: () => {
    +      throw new Error(
    +        'CoffeeScript execution in frontmatter is not allowed for security reasons'
    +      );
    +    },
    +  },
    +  coffeescript: {
    +    parse: () => {
    +      throw new Error(
    +        'CoffeeScript execution in frontmatter is not allowed for security reasons'
    +      );
    +    },
    +    stringify: () => {
    +      throw new Error(
    +        'CoffeeScript execution in frontmatter is not allowed for security reasons'
    +      );
    +    },
    +  },
     };
     
     export const stringifyFile = (
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.